├── .github ├── FUNDING.yml └── workflows │ └── build.yml ├── .gitignore ├── .prettierrc ├── CHANGELOG.md ├── LICENSE ├── README.md ├── benchmark ├── benchmark.ts ├── old │ ├── html4-entities.ts │ ├── html5-entities.ts │ ├── index.ts │ └── xml-entities.ts └── package.json ├── eslint.config.mjs ├── jest.config.ts ├── package-lock.json ├── package.json ├── src ├── index.ts ├── named-references.ts ├── numeric-unicode-map.ts └── surrogate-pairs.ts ├── test ├── index.test.ts └── test-apps │ ├── commonjs │ ├── package.json │ └── test.js │ ├── esm │ ├── package.json │ └── test.js │ └── ts │ ├── package.json │ └── test.ts ├── tools ├── named-references.source.json └── process-named-references.ts ├── tsconfig.esm.json └── tsconfig.json /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [mdevils] 2 | tidelift: "npm/html-entities" 3 | -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: build 2 | on: [push, pull_request] 3 | jobs: 4 | build: 5 | runs-on: ubuntu-latest 6 | strategy: 7 | matrix: 8 | node-version: [20.x] 9 | steps: 10 | - uses: actions/checkout@v2 11 | - name: Use Node.js ${{ matrix.node-version }} 12 | uses: actions/setup-node@v1 13 | with: 14 | node-version: ${{ matrix.node-version }} 15 | - run: npm ci --prefer-offline --no-audit --no-update-notifier 16 | - run: npm run lint 17 | - run: npm run build 18 | - run: npm run test 19 | - run: npm run test:dist 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | npm-debug.log 2 | node_modules 3 | coverage.html 4 | dist 5 | .tshy 6 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "arrowParens": "always", 3 | "bracketSpacing": false, 4 | "endOfLine": "lf", 5 | "jsxBracketSameLine": true, 6 | "jsxSingleQuote": true, 7 | "parser": "typescript", 8 | "printWidth": 120, 9 | "proseWrap": "preserve", 10 | "semi": true, 11 | "singleQuote": true, 12 | "tabWidth": 4, 13 | "trailingComma": "none", 14 | 15 | "overrides": [ 16 | { 17 | "files": ["*.json", ".eslintrc", ".prettierrc"], 18 | "options": { 19 | "parser": "json", 20 | "singleQuote": false, 21 | "trailingComma": "none", 22 | "useTabs": false, 23 | "tabWidth": 2 24 | } 25 | } 26 | ] 27 | } 28 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines. 4 | 5 | ## [2.6.0](https://github.com/mdevils/html-entities/compare/v2.5.6...v2.6.0) (2025-03-30) 6 | 7 | 8 | ### Features 9 | 10 | * refactor build to add ESM export ([1e7071a](https://github.com/mdevils/html-entities/commit/1e7071aa53a26de8c408c0456bc1313cd8b8bd53)) 11 | * support ESM, fixes [#95](https://github.com/mdevils/html-entities/issues/95) ([a466440](https://github.com/mdevils/html-entities/commit/a4664403c3a2df66c4c29a5253d510a75967f103)) 12 | 13 | ### [2.5.6](https://github.com/mdevils/html-entities/compare/v2.5.5...v2.5.6) (2025-03-30) 14 | 15 | 16 | ### Bug Fixes 17 | 18 | * significantly reduce package size ([72c7819](https://github.com/mdevils/html-entities/commit/72c78196a24c7c942367aeac248cd0f73a3e8b20)) 19 | 20 | ### [2.5.5](https://github.com/mdevils/html-entities/compare/v2.5.4...v2.5.5) (2025-03-28) 21 | 22 | 23 | ### Bug Fixes 24 | 25 | * incorrect typescript dependency ([adb6176](https://github.com/mdevils/html-entities/commit/adb61760607179251d9249ac22ed556289bdce3a)) 26 | 27 | ### [2.5.4](https://github.com/mdevils/html-entities/compare/v2.5.3...v2.5.4) (2025-03-28) 28 | 29 | 30 | ### Bug Fixes 31 | 32 | * avoid failing on numbers, fixes [#97](https://github.com/mdevils/html-entities/issues/97) ([421fd99](https://github.com/mdevils/html-entities/commit/421fd993dc50c173bd28bc22363d3e455e9c84ad)) 33 | 34 | ### [2.5.3](https://github.com/mdevils/html-entities/compare/v2.5.2...v2.5.3) (2025-03-23) 35 | 36 | 37 | ### Bug Fixes 38 | 39 | * improve encoding performance ([f4d14e2](https://github.com/mdevils/html-entities/commit/f4d14e2bd3038d8af5b98321c5021ccc346fa936)) 40 | 41 | 2.5.0 42 | ----- 43 | 44 | * Include source maps into the npm package. 45 | 46 | 2.4.0 47 | ----- 48 | 49 | * Introduce `nonAsciiPrintableOnly` mode. 50 | 51 | 2.3.5 52 | ----- 53 | 54 | * Reduce unnecessarily long RegExps. 55 | 56 | 2.3.4 57 | ----- 58 | 59 | * Fix the problem decoding HTML5 entities those names are part of HTML4 entities. 60 | 61 | 2.3.3 62 | ----- 63 | 64 | * `package.json`: specify `sideEffects: false` 65 | 66 | 2.3.2 67 | ----- 68 | 69 | * Minimize data files, remove unnecessary files. 70 | 71 | 2.3.1 72 | ----- 73 | 74 | * Improve performance of `encode()`, `decode()` and `decodeEntity()` by using function inlining. 75 | * Fix decoding HEX HTML entities in some cases. 76 | 77 | 2.3.0 78 | ----- 79 | 80 | * Add flow types. 81 | 82 | 2.2.0 83 | ----- 84 | 85 | * A fast `decodeEntity()` method to decode a single HTML entity. 86 | 87 | 2.1.1 88 | ----- 89 | 90 | * Speed up both `encode()` and `decode()` methods. 91 | 92 | 2.1.0 93 | ----- 94 | 95 | * Add `extensive` mode to `encode()` method. This mode encodes all non-printable characters, non-ASCII characters and all characters with named references. 96 | 97 | 2.0.6 98 | ----- 99 | 100 | * Handle invalid numeric HTML entities: mimic browser behaviour. 101 | 102 | 2.0.5 103 | ----- 104 | 105 | * Handling behaviour of ambiguous ampersands. 106 | 107 | 2.0.4 108 | ----- 109 | 110 | * Fix webpack build warning. 111 | 112 | 2.0.3 113 | ----- 114 | 115 | * Handle invalid numeric HTML entities. 116 | 117 | 2.0.2 118 | ----- 119 | 120 | * Handle `null` and `undefined` text values. 121 | 122 | 2.0.1 123 | ----- 124 | 125 | * Fix decoding numeric HTML entities. 126 | 127 | 2.0.0 128 | ----- 129 | 130 | * Performance was greatly improved. 131 | * New API: simpler and more flexible. 132 | 133 | `htmlEntitiesInstance.encode(text)` -> `encode(text)` 134 | 135 | Before: 136 | 137 | ```js 138 | import {AllHtmlEntities} from 'html-entities'; 139 | 140 | const entities = new AllHtmlEntities(); 141 | console.log( 142 | entities.encode('') 143 | ); 144 | ``` 145 | 146 | After: 147 | 148 | ```js 149 | import {encode} from 'html-entities'; 150 | 151 | console.log( 152 | encode('') 153 | ); 154 | ``` 155 | 156 | --- 157 | 158 | `instance.encodeNonASCII(text)` -> `encode(text, {mode: 'nonAscii'})` 159 | 160 | Before: 161 | 162 | ```js 163 | import {AllHtmlEntities} from 'html-entities'; 164 | 165 | const entities = new AllHtmlEntities(); 166 | console.log( 167 | entities.encodeNonASCII('& © ∆') 168 | ); 169 | ``` 170 | 171 | After: 172 | 173 | ```js 174 | import {encode} from 'html-entities'; 175 | 176 | console.log( 177 | encode('& © ∆', {mode: 'nonAscii'}) 178 | ); 179 | ``` 180 | 181 | --- 182 | 183 | `instance.encodeNonASCII(text)` -> `encode(text, {mode: 'nonAsciiPrintable'})` 184 | 185 | Before: 186 | 187 | ```js 188 | import {AllHtmlEntities} from 'html-entities'; 189 | 190 | const entities = new AllHtmlEntities(); 191 | console.log( 192 | entities.encodeNonASCII('& © ∆ \x01') 193 | ); 194 | ``` 195 | 196 | After: 197 | 198 | ```js 199 | import {encode} from 'html-entities'; 200 | 201 | console.log( 202 | encode('& © ∆ \x01', {mode: 'nonAsciiPrintable'}) 203 | ); 204 | ``` 205 | 206 | --- 207 | 208 | `instance.decode(text)` -> `decode(text)` 209 | 210 | Before: 211 | 212 | ```js 213 | import {AllHtmlEntities} from 'html-entities'; 214 | 215 | const entities = new AllHtmlEntities(); 216 | console.log( 217 | entities.decode('<>&') 218 | ); 219 | ``` 220 | 221 | After: 222 | 223 | ```js 224 | import {decode} from 'html-entities'; 225 | 226 | console.log( 227 | decode('<>&') 228 | ); 229 | ``` 230 | 231 | --- 232 | 233 | Different XML/HTML versions are now implemented via options instead of different classes. 234 | 235 | Before: 236 | 237 | ```js 238 | import {XmlEntities, Html4Entities, Html5Entities, AllHtmlEntities} from 'html-entities'; 239 | 240 | const xmlEntities = new XmlEntities(); 241 | const html4Entities = new Html4Entities(); 242 | const html5Entities = new Html5Entities(); 243 | const allHtmlEntities = new AllHtmlEntities(); 244 | 245 | console.log(xmlEntities.encode('<>&')); 246 | console.log(html4Entities.encode('<>&©')); 247 | console.log(html5Entities.encode('<>&©℞')); 248 | console.log(allHtmlEntities.encode('<>&©℞')); 249 | 250 | console.log(xmlEntities.decode('<>&')); 251 | console.log(html4Entities.decode('<>&©')); 252 | console.log(html5Entities.decode('<>&©℞')); 253 | console.log(allHtmlEntities.decode('<>&©℞')); 254 | ``` 255 | 256 | After: 257 | 258 | ```js 259 | import {encode, decode} from 'html-entities'; 260 | 261 | console.log(encode('<>&', {level: 'xml'})); 262 | console.log(encode('<>&©', {level: 'html4', mode: 'nonAscii'})); 263 | console.log(encode('<>&©℞', {level: 'html5', mode: 'nonAscii'})); 264 | console.log(encode('<>&©℞', {level: 'all', mode: 'nonAscii'})); 265 | 266 | console.log(decode('<>&', {level: 'xml'})); 267 | console.log(decode('<>&©', {level: 'html4'})); 268 | console.log(decode('<>&©℞', {level: 'html5'})); 269 | console.log(decode('<>&©℞', {level: 'all'})); 270 | ``` 271 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2021 Dulin Marat 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # html-entities 2 | 3 | [![npm version](https://img.shields.io/npm/v/html-entities.svg)](https://www.npmjs.com/package/html-entities) 4 | [![License](https://img.shields.io/npm/l/html-entities.svg)](https://github.com/mdevils/html-entities/blob/main/LICENSE) 5 | [![Types](https://img.shields.io/npm/types/html-entities.svg)](https://www.npmjs.com/package/html-entities) 6 | [![Bundle size](https://img.shields.io/bundlephobia/minzip/html-entities)](https://bundlephobia.com/package/html-entities) 7 | 8 | Fast and lightweight HTML entities library for all HTML standards (HTML5, HTML4, XML). 9 | 10 | ## Features 11 | 12 | - 🚀 **Fast performance** - optimized for speed (see [benchmarks](#performance-comparison)) 13 | - 📦 **Lightweight** - small package size (132 kB, see [comparison](#package-size-comparison)) 14 | - 🔄 **Standards compliant** - supports HTML5, HTML4, and XML entities 15 | - 📄 **Type safe** - includes TypeScript and Flow types 16 | - 🔌 **Dual ESM/CJS support** - works with both module systems 17 | 18 | ## Installation 19 | 20 | ```bash 21 | # NPM 22 | npm install html-entities 23 | 24 | # Yarn 25 | yarn add html-entities 26 | 27 | # pnpm 28 | pnpm add html-entities 29 | ``` 30 | 31 | ## Usage 32 | 33 | ### encode(text, options) 34 | 35 | Encodes text by replacing HTML special characters (`<>&"'`) and/or other character ranges depending on `mode` option. 36 | 37 | ```js 38 | import { encode } from 'html-entities'; 39 | 40 | // Basic usage (defaults to HTML5 special chars only) 41 | encode('< > " \' & © ∆'); 42 | // -> '< > " ' & © ∆' 43 | 44 | // Encode non-ASCII printable characters 45 | encode('< ©', { mode: 'nonAsciiPrintable' }); 46 | // -> '< ©' 47 | 48 | // Specify encoding level (XML) and mode 49 | encode('< ©', { mode: 'nonAsciiPrintable', level: 'xml' }); 50 | // -> '< ©' 51 | 52 | // Encode only non-ASCII characters, leaving special chars intact 53 | encode('< > " \' & ©', { mode: 'nonAsciiPrintableOnly', level: 'xml' }); 54 | // -> '< > " \' & ©' 55 | ``` 56 | 57 | #### Options 58 | 59 | ##### `level` 60 | - `all` or `html5` (default) - Uses HTML5 named references 61 | - `html4` - Uses HTML4 named references 62 | - `xml` - Uses XML named references 63 | 64 | ##### `mode` 65 | - `specialChars` (default) - Encodes only HTML special characters 66 | - `nonAscii` - Encodes HTML special characters and everything outside the [ASCII range](https://en.wikipedia.org/wiki/ASCII) 67 | - `nonAsciiPrintable` - Encodes HTML special characters and everything outside [ASCII printable characters](https://en.wikipedia.org/wiki/ASCII#Printable_characters) 68 | - `nonAsciiPrintableOnly` - Encodes everything outside ASCII printable range, keeping HTML special characters intact 69 | - `extensive` - Encodes all non-printable characters, non-ASCII characters and all characters with named references 70 | 71 | ##### `numeric` 72 | - `decimal` (default) - Uses decimal numbers for HTML entities (e.g., `©`) 73 | - `hexadecimal` - Uses hexadecimal numbers for HTML entities (e.g., `©`) 74 | 75 | ### decode(text, options) 76 | 77 | Decodes text by replacing HTML entities with their corresponding characters. Unknown entities are preserved. 78 | 79 | ```js 80 | import { decode } from 'html-entities'; 81 | 82 | // Basic usage (defaults to HTML5) 83 | decode('< > " ' & © ∆'); 84 | // -> '< > " \' & © ∆' 85 | 86 | // Specify encoding level 87 | decode('©', { level: 'html5' }); 88 | // -> '©' 89 | 90 | // XML level doesn't recognize © 91 | decode('©', { level: 'xml' }); 92 | // -> '©' 93 | ``` 94 | 95 | #### Options 96 | 97 | ##### `level` 98 | - `all` or `html5` (default) - Uses HTML5 named references 99 | - `html4` - Uses HTML4 named references 100 | - `xml` - Uses XML named references 101 | 102 | ##### `scope` 103 | - `body` (default) - Emulates browser behavior when parsing tag bodies: entities without semicolon are also replaced 104 | - `attribute` - Emulates browser behavior when parsing tag attributes: entities without semicolon are replaced when not followed by `=` 105 | - `strict` - Ignores entities without semicolon 106 | 107 | ### decodeEntity(text, options) 108 | 109 | Decodes a single HTML entity. Unknown entities are preserved. 110 | 111 | ```js 112 | import { decodeEntity } from 'html-entities'; 113 | 114 | decodeEntity('<'); 115 | // -> '<' 116 | 117 | decodeEntity('©', { level: 'html5' }); 118 | // -> '©' 119 | 120 | decodeEntity('©', { level: 'xml' }); 121 | // -> '©' 122 | ``` 123 | 124 | #### Options 125 | 126 | ##### `level` 127 | - `all` or `html5` (default) - Uses HTML5 named references 128 | - `html4` - Uses HTML4 named references 129 | - `xml` - Uses XML named references 130 | 131 | ## Package Size Comparison 132 | 133 | | Library | Size | 134 | |---------|------| 135 | | **html-entities** | 132 kB | 136 | | he | 124 kB | 137 | | entities | 540 kB | 138 | 139 | ## Performance Comparison 140 | 141 | Results from benchmarks using `benchmark.js`. This library's results are marked with `*`. 142 | See benchmark source code at `benchmark/benchmark.ts`. 143 | 144 | ``` 145 | Common 146 | 147 | Initialization / Load speed 148 | 149 | #1: he x 516 ops/sec ±5.71% (78 runs sampled) 150 | * #2: html-entities x 407 ops/sec ±5.64% (81 runs sampled) 151 | #3: entities x 352 ops/sec ±4.16% (80 runs sampled) 152 | 153 | HTML5 154 | 155 | Encode test 156 | 157 | * #1: html-entities.encode - html5, extensive x 437,236 ops/sec ±0.90% (98 runs sampled) 158 | #2: entities.encodeHTML x 335,714 ops/sec ±0.87% (92 runs sampled) 159 | 160 | Encode non-ASCII test 161 | 162 | * #1: html-entities.encode - html5, nonAscii x 749,246 ops/sec ±0.61% (96 runs sampled) 163 | #2: entities.encodeNonAsciiHTML x 706,984 ops/sec ±1.06% (98 runs sampled) 164 | * #3: html-entities.encode - html5, nonAsciiPrintable x 691,193 ops/sec ±4.47% (90 runs sampled) 165 | #4: he.encode x 141,105 ops/sec ±0.87% (92 runs sampled) 166 | 167 | Decode test 168 | 169 | #1: entities.decodeHTML x 678,595 ops/sec ±1.28% (92 runs sampled) 170 | #2: entities.decodeHTMLStrict x 684,372 ops/sec ±2.76% (82 runs sampled) 171 | * #3: html-entities.decode - html5, strict x 485,664 ops/sec ±0.80% (94 runs sampled) 172 | * #4: html-entities.decode - html5, body x 463,074 ops/sec ±1.11% (93 runs sampled) 173 | * #5: html-entities.decode - html5, attribute x 456,185 ops/sec ±2.24% (91 runs sampled) 174 | #6: he.decode x 302,668 ops/sec ±2.73% (90 runs sampled) 175 | 176 | HTML4 177 | 178 | Encode test 179 | 180 | * #1: html-entities.encode - html4, nonAscii x 737,475 ops/sec ±1.04% (95 runs sampled) 181 | * #2: html-entities.encode - html4, nonAsciiPrintable x 649,866 ops/sec ±4.28% (79 runs sampled) 182 | * #3: html-entities.encode - html4, extensive x 202,337 ops/sec ±3.66% (64 runs sampled) 183 | 184 | Decode test 185 | 186 | * #1: html-entities.decode - html4, attribute x 529,674 ops/sec ±0.90% (90 runs sampled) 187 | * #2: html-entities.decode - html4, body x 499,135 ops/sec ±2.27% (80 runs sampled) 188 | * #3: html-entities.decode - html4, strict x 489,806 ops/sec ±4.37% (84 runs sampled) 189 | 190 | XML 191 | 192 | Encode test 193 | 194 | * #1: html-entities.encode - xml, nonAscii x 823,097 ops/sec ±0.75% (81 runs sampled) 195 | * #2: html-entities.encode - xml, nonAsciiPrintable x 764,638 ops/sec ±0.93% (93 runs sampled) 196 | #3: entities.encodeXML x 672,186 ops/sec ±1.51% (92 runs sampled) 197 | * #4: html-entities.encode - xml, extensive x 376,870 ops/sec ±0.76% (77 runs sampled) 198 | 199 | Decode test 200 | 201 | #1: entities.decodeXML x 930,758 ops/sec ±2.90% (90 runs sampled) 202 | * #2: html-entities.decode - xml, body x 617,321 ops/sec ±0.74% (83 runs sampled) 203 | * #3: html-entities.decode - xml, attribute x 611,598 ops/sec ±0.50% (92 runs sampled) 204 | * #4: html-entities.decode - xml, strict x 607,191 ops/sec ±2.30% (85 runs sampled) 205 | 206 | Escaping 207 | 208 | Escape test 209 | 210 | #1: entities.escapeUTF8 x 1,930,874 ops/sec ±0.80% (95 runs sampled) 211 | #2: he.escape x 1,717,522 ops/sec ±0.75% (84 runs sampled) 212 | * #3: html-entities.encode - xml, specialChars x 1,611,374 ops/sec ±1.30% (92 runs sampled) 213 | #4: entities.escape x 673,710 ops/sec ±1.30% (94 runs sampled) 214 | ``` 215 | 216 | ## License 217 | 218 | MIT 219 | 220 | ## Security Contact Information 221 | 222 | To report a security vulnerability, please use the [Tidelift security contact](https://tidelift.com/security). Tidelift will coordinate the fix and disclosure. 223 | 224 | ## For Enterprise 225 | 226 | Available as part of the Tidelift Subscription. 227 | 228 | The maintainers of `html-entities` and thousands of other packages are working with Tidelift to deliver commercial support and maintenance for the open source dependencies you use to build your applications. Save time, reduce risk, and improve code health, while paying the maintainers of the exact dependencies you use. [Learn more.](https://tidelift.com/subscription/pkg/npm-html-entities?utm_source=npm-html-entities&utm_medium=referral&utm_campaign=enterprise) -------------------------------------------------------------------------------- /benchmark/benchmark.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | import * as Benchmark from 'benchmark'; 3 | import * as entities from 'entities'; 4 | import * as he from 'he'; 5 | import {XmlEntities, Html4Entities, Html5Entities} from './old'; 6 | import {decode, DecodeOptions, encode, EncodeOptions} from '../src'; 7 | 8 | const includeOldTests = Boolean(process.env.INCLUDE_OLD); 9 | 10 | const xmlEntities = new XmlEntities(); 11 | const html4Entities = new Html4Entities(); 12 | const html5Entities = new Html5Entities(); 13 | 14 | type GlobalWithRequire = typeof global & {require: typeof require}; 15 | 16 | (global as GlobalWithRequire).require = require; 17 | 18 | function cleanNodeModulesCache() { 19 | const cache = (global as GlobalWithRequire).require.cache; 20 | for (const cacheKey in cache) { 21 | delete cache[cacheKey]; 22 | } 23 | } 24 | 25 | function createRequireTests(modules: Record) { 26 | return Object.keys(modules).reduce((result, moduleName) => { 27 | result[moduleName] = function() { 28 | require(modules[moduleName]); 29 | cleanNodeModulesCache(); 30 | }; 31 | return result; 32 | }, {} as Record void>); 33 | } 34 | 35 | function createHtml5EncodeMethodsNonAscii(textToEncode: string) { 36 | const heOptions = {useNamedReferences: true}; 37 | const nonAsciiPrintable: EncodeOptions = {level: 'html5', mode: 'nonAsciiPrintable'}; 38 | const nonAscii: EncodeOptions = {level: 'html5', mode: 'nonAscii'}; 39 | return { 40 | 'html-entities.encode - html5, nonAscii': () => encode(textToEncode, nonAscii), 41 | 'html-entities.encode - html5, nonAsciiPrintable': () => encode(textToEncode, nonAsciiPrintable), 42 | 'entities.encodeNonAsciiHTML': () => entities.encodeNonAsciiHTML(textToEncode), 43 | 'he.encode': () => he.encode(textToEncode, heOptions) 44 | }; 45 | } 46 | 47 | function createHtml5EncodeMethods(textToEncode: string) { 48 | const extensive: EncodeOptions = {level: 'html5', mode: 'extensive'}; 49 | return { 50 | 'html-entities.encode - html5, extensive': () => encode(textToEncode, extensive), 51 | 'entities.encodeHTML': () => entities.encodeHTML(textToEncode) 52 | }; 53 | } 54 | 55 | function createHtml5DecodeMethods(textToDecode: string) { 56 | const strict: DecodeOptions = {level: 'html5', scope: 'strict'}; 57 | const body: DecodeOptions = {level: 'html5', scope: 'body'}; 58 | const attribute: DecodeOptions = {level: 'html5', scope: 'attribute'}; 59 | return { 60 | 'html-entities.decode - html5, strict': () => decode(textToDecode, strict), 61 | 'html-entities.decode - html5, body': () => decode(textToDecode, body), 62 | 'html-entities.decode - html5, attribute': () => decode(textToDecode, attribute), 63 | '(old) Html5Entities.decode': () => html5Entities.decode(textToDecode), 64 | 'entities.decodeHTML': () => entities.decodeHTML(textToDecode), 65 | 'entities.decodeHTMLStrict': () => entities.decodeHTMLStrict(textToDecode), 66 | 'he.decode': () => he.decode(textToDecode) 67 | }; 68 | } 69 | 70 | function createHtml4EncodeMethods(textToEncode: string) { 71 | const nonAsciiPrintable: EncodeOptions = {level: 'html4', mode: 'nonAsciiPrintable'}; 72 | const extensive: EncodeOptions = {level: 'html4', mode: 'extensive'}; 73 | const nonAscii: EncodeOptions = {level: 'html4', mode: 'nonAscii'}; 74 | return { 75 | 'html-entities.encode - html4, nonAscii': () => encode(textToEncode, nonAscii), 76 | 'html-entities.encode - html4, nonAsciiPrintable': () => encode(textToEncode, nonAsciiPrintable), 77 | 'html-entities.encode - html4, extensive': () => encode(textToEncode, extensive), 78 | '(old) Html4Entities.encodeNonASCII': () => html4Entities.encodeNonASCII(textToEncode) 79 | }; 80 | } 81 | 82 | function createHtml4DecodeMethods(textToDecode: string) { 83 | const strict: DecodeOptions = {level: 'html4', scope: 'strict'}; 84 | const body: DecodeOptions = {level: 'html4', scope: 'body'}; 85 | const attribute: DecodeOptions = {level: 'html4', scope: 'attribute'}; 86 | return { 87 | 'html-entities.decode - html4, strict': () => decode(textToDecode, strict), 88 | 'html-entities.decode - html4, body': () => decode(textToDecode, body), 89 | 'html-entities.decode - html4, attribute': () => decode(textToDecode, attribute), 90 | '(old) Html4Entities.decode': () => html4Entities.decode(textToDecode) 91 | }; 92 | } 93 | 94 | function createXmlEncodeMethods(textToEncode: string) { 95 | const nonAsciiPrintable: EncodeOptions = {level: 'xml', mode: 'nonAsciiPrintable'}; 96 | const extensive: EncodeOptions = {level: 'xml', mode: 'extensive'}; 97 | const nonAscii: EncodeOptions = {level: 'xml', mode: 'nonAscii'}; 98 | return { 99 | 'html-entities.encode - xml, nonAscii': () => encode(textToEncode, nonAscii), 100 | 'html-entities.encode - xml, nonAsciiPrintable': () => encode(textToEncode, nonAsciiPrintable), 101 | 'html-entities.encode - xml, extensive': () => encode(textToEncode, extensive), 102 | '(old) XmlEntities.encodeNonASCII': () => xmlEntities.encode(textToEncode), 103 | 'entities.encodeXML': () => entities.encodeXML(textToEncode) 104 | }; 105 | } 106 | 107 | function createXmlDecodeMethods(textToDecode: string) { 108 | const strict: DecodeOptions = {level: 'xml', scope: 'strict'}; 109 | const body: DecodeOptions = {level: 'xml', scope: 'body'}; 110 | const attribute: DecodeOptions = {level: 'xml', scope: 'attribute'}; 111 | return { 112 | 'html-entities.decode - xml, strict': () => decode(textToDecode, strict), 113 | 'html-entities.decode - xml, body': () => decode(textToDecode, body), 114 | 'html-entities.decode - xml, attribute': () => decode(textToDecode, attribute), 115 | '(old) XmlEntities.decode': () => xmlEntities.decode(textToDecode), 116 | 'entities.decodeXML': () => entities.decodeXML(textToDecode) 117 | }; 118 | } 119 | 120 | function createEscapeMethods(textToEncode: string) { 121 | const specialChars: EncodeOptions = {level: 'xml', mode: 'specialChars'}; 122 | return { 123 | 'html-entities.encode - xml, specialChars': () => encode(textToEncode, specialChars), 124 | '(old) XmlEntities.encode': () => xmlEntities.encode(textToEncode), 125 | 'he.escape': () => he.escape(textToEncode), 126 | 'entities.escapeUTF8': () => entities.escapeUTF8(textToEncode), 127 | 'entities.escape': () => entities.escape(textToEncode) 128 | }; 129 | } 130 | 131 | function section(sectionName: string, callback: () => void) { 132 | console.log(sectionName + '\n'); 133 | callback(); 134 | } 135 | 136 | const indent = ' '; 137 | const indentWithPointer = ' * '; 138 | 139 | function benchmark(name: string, tests: {[key: string]: () => void}) { 140 | console.log(`${indent}${name}\n`); 141 | const suite = new Benchmark.Suite(); 142 | for (const [testName, testCallback] of Object.entries(tests)) { 143 | if (!includeOldTests && testName.indexOf('(old)') !== -1) { 144 | continue; 145 | } 146 | suite.add(testName, testCallback); 147 | } 148 | suite.on('complete', function (this: Benchmark.Suite) { 149 | const benchmarks = Array.from((this as unknown) as Benchmark[]); 150 | benchmarks.sort(function ({stats: statsA}: Benchmark, {stats: statsB}: Benchmark) { 151 | return statsA.mean + statsA.moe > statsB.mean + statsB.moe ? 1 : -1; 152 | }); 153 | for (let i = 0; i < benchmarks.length; i++) { 154 | const benchmarkText = String(benchmarks[i]); 155 | const secondIndent = benchmarkText.includes('html-entities') ? indentWithPointer : indent; 156 | console.log(`${indent}${secondIndent}#${i + 1}: ${benchmarkText}`); 157 | } 158 | }); 159 | suite.on('error', function (event: {target: Benchmark}) { 160 | console.error(event.target.error); 161 | }); 162 | suite.run(); 163 | console.log(); 164 | } 165 | 166 | section('Common', () => { 167 | benchmark( 168 | 'Initialization / Load speed', 169 | createRequireTests({ 170 | 'html-entities': '../lib', 171 | entities: 'entities', 172 | he: 'he' 173 | }), 174 | // cleanNodeModulesCache 175 | ); 176 | }); 177 | section('HTML5', () => { 178 | benchmark( 179 | 'Encode test', 180 | createHtml5EncodeMethods( 181 | ` 182 | This is a test encode benchmark. 183 | Should contain <>&' and ". 184 | Some control characters: \x01. 185 | Some HTML5-only named references: ℞ ⪶. 186 | And some unicode symbols: ©, ∆, —, 😂. 187 | Good luck. 188 | ` 189 | ) 190 | ); 191 | benchmark( 192 | 'Encode non-ASCII test', 193 | createHtml5EncodeMethodsNonAscii( 194 | ` 195 | This is a test encode benchmark. 196 | Should contain <>&' and ". 197 | Some control characters: \x01. 198 | Some HTML5-only named references: ℞ ⪶. 199 | And some unicode symbols: ©, ∆, —, 😂. 200 | Good luck. 201 | ` 202 | ) 203 | ); 204 | benchmark( 205 | 'Decode test', 206 | createHtml5DecodeMethods( 207 | ` 208 | <This> is a test encode benchmark. 209 | Should contain <>&' and ". 210 | Some control characters: . 211 | Some HTML5-only named references: ℞ ⪶. 212 | And some unicode symbols: ©, ∆, —, 😂. 213 | Good luck. 214 | ` 215 | ) 216 | ); 217 | }); 218 | section('HTML4', () => { 219 | benchmark( 220 | 'Encode test', 221 | createHtml4EncodeMethods( 222 | ` 223 | This is a test encode benchmark. 224 | Should contain <>&' and ". 225 | Some control characters: \x01. 226 | Some HTML4 named references: º ». 227 | And some unicode symbols: ©, ∆, —, 😂. 228 | Good luck. 229 | ` 230 | ) 231 | ); 232 | benchmark( 233 | 'Decode test', 234 | createHtml4DecodeMethods( 235 | ` 236 | <This> is a test encode benchmark. 237 | Should contain <>&' and ". 238 | Some control characters: . 239 | Some HTML4 named references: º ». 240 | And some unicode symbols: ©, ∆, —, 😂. 241 | Good luck. 242 | ` 243 | ) 244 | ); 245 | }); 246 | 247 | section('XML', () => { 248 | benchmark( 249 | 'Encode test', 250 | createXmlEncodeMethods( 251 | ` 252 | This is a test encode benchmark. 253 | Should contain <>&' and ". 254 | Some control characters: \x01. 255 | And some unicode symbols: ©, ∆, —, 😂. 256 | Good luck. 257 | ` 258 | ) 259 | ); 260 | benchmark( 261 | 'Decode test', 262 | createXmlDecodeMethods( 263 | ` 264 | <This> is a test encode benchmark. 265 | Should contain <>&' and ". 266 | Some control characters: . 267 | And some unicode symbols: ∆, 😂. 268 | Good luck. 269 | ` 270 | ) 271 | ); 272 | }); 273 | 274 | section('Escaping', () => { 275 | benchmark( 276 | 'Escape test', 277 | createEscapeMethods( 278 | ` 279 | This is a test encode benchmark. 280 | Should contain <>&' and ". 281 | Some control characters: \x01. 282 | And some unicode symbols: ©, ∆, —, 😂. 283 | Good luck. 284 | ` 285 | ) 286 | ); 287 | }); 288 | -------------------------------------------------------------------------------- /benchmark/old/html4-entities.ts: -------------------------------------------------------------------------------- 1 | import {fromCodePoint, getCodePoint, highSurrogateFrom, highSurrogateTo} from '../../src/surrogate-pairs'; 2 | 3 | const HTML_ALPHA = ['apos', 'nbsp', 'iexcl', 'cent', 'pound', 'curren', 'yen', 'brvbar', 'sect', 'uml', 'copy', 'ordf', 'laquo', 'not', 'shy', 'reg', 'macr', 'deg', 'plusmn', 'sup2', 'sup3', 'acute', 'micro', 'para', 'middot', 'cedil', 'sup1', 'ordm', 'raquo', 'frac14', 'frac12', 'frac34', 'iquest', 'Agrave', 'Aacute', 'Acirc', 'Atilde', 'Auml', 'Aring', 'AElig', 'Ccedil', 'Egrave', 'Eacute', 'Ecirc', 'Euml', 'Igrave', 'Iacute', 'Icirc', 'Iuml', 'ETH', 'Ntilde', 'Ograve', 'Oacute', 'Ocirc', 'Otilde', 'Ouml', 'times', 'Oslash', 'Ugrave', 'Uacute', 'Ucirc', 'Uuml', 'Yacute', 'THORN', 'szlig', 'agrave', 'aacute', 'acirc', 'atilde', 'auml', 'aring', 'aelig', 'ccedil', 'egrave', 'eacute', 'ecirc', 'euml', 'igrave', 'iacute', 'icirc', 'iuml', 'eth', 'ntilde', 'ograve', 'oacute', 'ocirc', 'otilde', 'ouml', 'divide', 'oslash', 'ugrave', 'uacute', 'ucirc', 'uuml', 'yacute', 'thorn', 'yuml', 'quot', 'amp', 'lt', 'gt', 'OElig', 'oelig', 'Scaron', 'scaron', 'Yuml', 'circ', 'tilde', 'ensp', 'emsp', 'thinsp', 'zwnj', 'zwj', 'lrm', 'rlm', 'ndash', 'mdash', 'lsquo', 'rsquo', 'sbquo', 'ldquo', 'rdquo', 'bdquo', 'dagger', 'Dagger', 'permil', 'lsaquo', 'rsaquo', 'euro', 'fnof', 'Alpha', 'Beta', 'Gamma', 'Delta', 'Epsilon', 'Zeta', 'Eta', 'Theta', 'Iota', 'Kappa', 'Lambda', 'Mu', 'Nu', 'Xi', 'Omicron', 'Pi', 'Rho', 'Sigma', 'Tau', 'Upsilon', 'Phi', 'Chi', 'Psi', 'Omega', 'alpha', 'beta', 'gamma', 'delta', 'epsilon', 'zeta', 'eta', 'theta', 'iota', 'kappa', 'lambda', 'mu', 'nu', 'xi', 'omicron', 'pi', 'rho', 'sigmaf', 'sigma', 'tau', 'upsilon', 'phi', 'chi', 'psi', 'omega', 'thetasym', 'upsih', 'piv', 'bull', 'hellip', 'prime', 'Prime', 'oline', 'frasl', 'weierp', 'image', 'real', 'trade', 'alefsym', 'larr', 'uarr', 'rarr', 'darr', 'harr', 'crarr', 'lArr', 'uArr', 'rArr', 'dArr', 'hArr', 'forall', 'part', 'exist', 'empty', 'nabla', 'isin', 'notin', 'ni', 'prod', 'sum', 'minus', 'lowast', 'radic', 'prop', 'infin', 'ang', 'and', 'or', 'cap', 'cup', 'int', 'there4', 'sim', 'cong', 'asymp', 'ne', 'equiv', 'le', 'ge', 'sub', 'sup', 'nsub', 'sube', 'supe', 'oplus', 'otimes', 'perp', 'sdot', 'lceil', 'rceil', 'lfloor', 'rfloor', 'lang', 'rang', 'loz', 'spades', 'clubs', 'hearts', 'diams']; 4 | const HTML_CODES = [39, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 34, 38, 60, 62, 338, 339, 352, 353, 376, 710, 732, 8194, 8195, 8201, 8204, 8205, 8206, 8207, 8211, 8212, 8216, 8217, 8218, 8220, 8221, 8222, 8224, 8225, 8240, 8249, 8250, 8364, 402, 913, 914, 915, 916, 917, 918, 919, 920, 921, 922, 923, 924, 925, 926, 927, 928, 929, 931, 932, 933, 934, 935, 936, 937, 945, 946, 947, 948, 949, 950, 951, 952, 953, 954, 955, 956, 957, 958, 959, 960, 961, 962, 963, 964, 965, 966, 967, 968, 969, 977, 978, 982, 8226, 8230, 8242, 8243, 8254, 8260, 8472, 8465, 8476, 8482, 8501, 8592, 8593, 8594, 8595, 8596, 8629, 8656, 8657, 8658, 8659, 8660, 8704, 8706, 8707, 8709, 8711, 8712, 8713, 8715, 8719, 8721, 8722, 8727, 8730, 8733, 8734, 8736, 8743, 8744, 8745, 8746, 8747, 8756, 8764, 8773, 8776, 8800, 8801, 8804, 8805, 8834, 8835, 8836, 8838, 8839, 8853, 8855, 8869, 8901, 8968, 8969, 8970, 8971, 9001, 9002, 9674, 9824, 9827, 9829, 9830]; 5 | 6 | const alphaIndex: {[key: string]: string} = {}; 7 | const numIndex : {[key: string]: string}= {}; 8 | 9 | (() => { 10 | let i = 0; 11 | const length = HTML_ALPHA.length; 12 | while (i < length) { 13 | const a = HTML_ALPHA[i]; 14 | const c = HTML_CODES[i]; 15 | alphaIndex[a] = String.fromCharCode(c); 16 | numIndex[c] = a; 17 | i++; 18 | } 19 | })(); 20 | 21 | export class Html4Entities { 22 | decode(str: string) { 23 | if (!str || !str.length) { 24 | return ''; 25 | } 26 | return str.replace(/&(#?[\w\d]+);?/g, function(s, entity) { 27 | let chr: string; 28 | if (entity.charAt(0) === "#") { 29 | const code = entity.charAt(1).toLowerCase() === 'x' ? 30 | parseInt(entity.substr(2), 16) : 31 | parseInt(entity.substr(1)); 32 | 33 | if (!isNaN(code) || code >= -32768) { 34 | if (code <= 65535) { 35 | chr = String.fromCharCode(code); 36 | } else { 37 | chr = fromCodePoint(code); 38 | } 39 | } 40 | } else { 41 | chr = alphaIndex[entity]; 42 | } 43 | return chr! || s; 44 | }); 45 | } 46 | 47 | static decode(str: string) { 48 | return new Html4Entities().decode(str); 49 | } 50 | 51 | encode(str: string) { 52 | if (!str || !str.length) { 53 | return ''; 54 | } 55 | const strLength = str.length; 56 | let result = ''; 57 | let i = 0; 58 | while (i < strLength) { 59 | const alpha = numIndex[str.charCodeAt(i)]; 60 | result += alpha ? "&" + alpha + ";" : str.charAt(i); 61 | i++; 62 | } 63 | return result; 64 | } 65 | 66 | static encode(str: string) { 67 | return new Html4Entities().encode(str); 68 | } 69 | 70 | encodeNonUTF(str: string) { 71 | if (!str || !str.length) { 72 | return ''; 73 | } 74 | const strLength = str.length; 75 | let result = ''; 76 | let i = 0; 77 | while (i < strLength) { 78 | const cc = str.charCodeAt(i); 79 | const alpha = numIndex[cc]; 80 | if (alpha) { 81 | result += "&" + alpha + ";"; 82 | } else if (cc < 32 || cc > 126) { 83 | if (cc >= highSurrogateFrom && cc <= highSurrogateTo) { 84 | result += '&#' + getCodePoint(str, i) + ';'; 85 | i++; 86 | } else { 87 | result += '&#' + cc + ';'; 88 | } 89 | } else { 90 | result += str.charAt(i); 91 | } 92 | i++; 93 | } 94 | return result; 95 | } 96 | 97 | static encodeNonUTF(str: string) { 98 | return new Html4Entities().encodeNonUTF(str); 99 | } 100 | 101 | encodeNonASCII(str: string) { 102 | if (!str || !str.length) { 103 | return ''; 104 | } 105 | const strLength = str.length; 106 | let result = ''; 107 | let i = 0; 108 | while (i < strLength) { 109 | const c = str.charCodeAt(i); 110 | if (c <= 255) { 111 | result += str[i++]; 112 | continue; 113 | } 114 | if (c >= highSurrogateFrom && c <= highSurrogateTo) { 115 | result += '&#' + getCodePoint(str, i) + ';'; 116 | i++; 117 | } else { 118 | result += '&#' + c + ';'; 119 | } 120 | i++; 121 | } 122 | return result; 123 | } 124 | 125 | static encodeNonASCII(str: string) { 126 | return new Html4Entities().encodeNonASCII(str); 127 | } 128 | } 129 | 130 | -------------------------------------------------------------------------------- /benchmark/old/html5-entities.ts: -------------------------------------------------------------------------------- 1 | import {fromCodePoint, getCodePoint, highSurrogateFrom, highSurrogateTo} from '../../src/surrogate-pairs'; 2 | 3 | const ENTITIES: [string, number[]][] = [['Aacute', [193]], ['aacute', [225]], ['Abreve', [258]], ['abreve', [259]], ['ac', [8766]], ['acd', [8767]], ['acE', [8766, 819]], ['Acirc', [194]], ['acirc', [226]], ['acute', [180]], ['Acy', [1040]], ['acy', [1072]], ['AElig', [198]], ['aelig', [230]], ['af', [8289]], ['Afr', [120068]], ['afr', [120094]], ['Agrave', [192]], ['agrave', [224]], ['alefsym', [8501]], ['aleph', [8501]], ['Alpha', [913]], ['alpha', [945]], ['Amacr', [256]], ['amacr', [257]], ['amalg', [10815]], ['amp', [38]], ['AMP', [38]], ['andand', [10837]], ['And', [10835]], ['and', [8743]], ['andd', [10844]], ['andslope', [10840]], ['andv', [10842]], ['ang', [8736]], ['ange', [10660]], ['angle', [8736]], ['angmsdaa', [10664]], ['angmsdab', [10665]], ['angmsdac', [10666]], ['angmsdad', [10667]], ['angmsdae', [10668]], ['angmsdaf', [10669]], ['angmsdag', [10670]], ['angmsdah', [10671]], ['angmsd', [8737]], ['angrt', [8735]], ['angrtvb', [8894]], ['angrtvbd', [10653]], ['angsph', [8738]], ['angst', [197]], ['angzarr', [9084]], ['Aogon', [260]], ['aogon', [261]], ['Aopf', [120120]], ['aopf', [120146]], ['apacir', [10863]], ['ap', [8776]], ['apE', [10864]], ['ape', [8778]], ['apid', [8779]], ['apos', [39]], ['ApplyFunction', [8289]], ['approx', [8776]], ['approxeq', [8778]], ['Aring', [197]], ['aring', [229]], ['Ascr', [119964]], ['ascr', [119990]], ['Assign', [8788]], ['ast', [42]], ['asymp', [8776]], ['asympeq', [8781]], ['Atilde', [195]], ['atilde', [227]], ['Auml', [196]], ['auml', [228]], ['awconint', [8755]], ['awint', [10769]], ['backcong', [8780]], ['backepsilon', [1014]], ['backprime', [8245]], ['backsim', [8765]], ['backsimeq', [8909]], ['Backslash', [8726]], ['Barv', [10983]], ['barvee', [8893]], ['barwed', [8965]], ['Barwed', [8966]], ['barwedge', [8965]], ['bbrk', [9141]], ['bbrktbrk', [9142]], ['bcong', [8780]], ['Bcy', [1041]], ['bcy', [1073]], ['bdquo', [8222]], ['becaus', [8757]], ['because', [8757]], ['Because', [8757]], ['bemptyv', [10672]], ['bepsi', [1014]], ['bernou', [8492]], ['Bernoullis', [8492]], ['Beta', [914]], ['beta', [946]], ['beth', [8502]], ['between', [8812]], ['Bfr', [120069]], ['bfr', [120095]], ['bigcap', [8898]], ['bigcirc', [9711]], ['bigcup', [8899]], ['bigodot', [10752]], ['bigoplus', [10753]], ['bigotimes', [10754]], ['bigsqcup', [10758]], ['bigstar', [9733]], ['bigtriangledown', [9661]], ['bigtriangleup', [9651]], ['biguplus', [10756]], ['bigvee', [8897]], ['bigwedge', [8896]], ['bkarow', [10509]], ['blacklozenge', [10731]], ['blacksquare', [9642]], ['blacktriangle', [9652]], ['blacktriangledown', [9662]], ['blacktriangleleft', [9666]], ['blacktriangleright', [9656]], ['blank', [9251]], ['blk12', [9618]], ['blk14', [9617]], ['blk34', [9619]], ['block', [9608]], ['bne', [61, 8421]], ['bnequiv', [8801, 8421]], ['bNot', [10989]], ['bnot', [8976]], ['Bopf', [120121]], ['bopf', [120147]], ['bot', [8869]], ['bottom', [8869]], ['bowtie', [8904]], ['boxbox', [10697]], ['boxdl', [9488]], ['boxdL', [9557]], ['boxDl', [9558]], ['boxDL', [9559]], ['boxdr', [9484]], ['boxdR', [9554]], ['boxDr', [9555]], ['boxDR', [9556]], ['boxh', [9472]], ['boxH', [9552]], ['boxhd', [9516]], ['boxHd', [9572]], ['boxhD', [9573]], ['boxHD', [9574]], ['boxhu', [9524]], ['boxHu', [9575]], ['boxhU', [9576]], ['boxHU', [9577]], ['boxminus', [8863]], ['boxplus', [8862]], ['boxtimes', [8864]], ['boxul', [9496]], ['boxuL', [9563]], ['boxUl', [9564]], ['boxUL', [9565]], ['boxur', [9492]], ['boxuR', [9560]], ['boxUr', [9561]], ['boxUR', [9562]], ['boxv', [9474]], ['boxV', [9553]], ['boxvh', [9532]], ['boxvH', [9578]], ['boxVh', [9579]], ['boxVH', [9580]], ['boxvl', [9508]], ['boxvL', [9569]], ['boxVl', [9570]], ['boxVL', [9571]], ['boxvr', [9500]], ['boxvR', [9566]], ['boxVr', [9567]], ['boxVR', [9568]], ['bprime', [8245]], ['breve', [728]], ['Breve', [728]], ['brvbar', [166]], ['bscr', [119991]], ['Bscr', [8492]], ['bsemi', [8271]], ['bsim', [8765]], ['bsime', [8909]], ['bsolb', [10693]], ['bsol', [92]], ['bsolhsub', [10184]], ['bull', [8226]], ['bullet', [8226]], ['bump', [8782]], ['bumpE', [10926]], ['bumpe', [8783]], ['Bumpeq', [8782]], ['bumpeq', [8783]], ['Cacute', [262]], ['cacute', [263]], ['capand', [10820]], ['capbrcup', [10825]], ['capcap', [10827]], ['cap', [8745]], ['Cap', [8914]], ['capcup', [10823]], ['capdot', [10816]], ['CapitalDifferentialD', [8517]], ['caps', [8745, 65024]], ['caret', [8257]], ['caron', [711]], ['Cayleys', [8493]], ['ccaps', [10829]], ['Ccaron', [268]], ['ccaron', [269]], ['Ccedil', [199]], ['ccedil', [231]], ['Ccirc', [264]], ['ccirc', [265]], ['Cconint', [8752]], ['ccups', [10828]], ['ccupssm', [10832]], ['Cdot', [266]], ['cdot', [267]], ['cedil', [184]], ['Cedilla', [184]], ['cemptyv', [10674]], ['cent', [162]], ['centerdot', [183]], ['CenterDot', [183]], ['cfr', [120096]], ['Cfr', [8493]], ['CHcy', [1063]], ['chcy', [1095]], ['check', [10003]], ['checkmark', [10003]], ['Chi', [935]], ['chi', [967]], ['circ', [710]], ['circeq', [8791]], ['circlearrowleft', [8634]], ['circlearrowright', [8635]], ['circledast', [8859]], ['circledcirc', [8858]], ['circleddash', [8861]], ['CircleDot', [8857]], ['circledR', [174]], ['circledS', [9416]], ['CircleMinus', [8854]], ['CirclePlus', [8853]], ['CircleTimes', [8855]], ['cir', [9675]], ['cirE', [10691]], ['cire', [8791]], ['cirfnint', [10768]], ['cirmid', [10991]], ['cirscir', [10690]], ['ClockwiseContourIntegral', [8754]], ['clubs', [9827]], ['clubsuit', [9827]], ['colon', [58]], ['Colon', [8759]], ['Colone', [10868]], ['colone', [8788]], ['coloneq', [8788]], ['comma', [44]], ['commat', [64]], ['comp', [8705]], ['compfn', [8728]], ['complement', [8705]], ['complexes', [8450]], ['cong', [8773]], ['congdot', [10861]], ['Congruent', [8801]], ['conint', [8750]], ['Conint', [8751]], ['ContourIntegral', [8750]], ['copf', [120148]], ['Copf', [8450]], ['coprod', [8720]], ['Coproduct', [8720]], ['copy', [169]], ['COPY', [169]], ['copysr', [8471]], ['CounterClockwiseContourIntegral', [8755]], ['crarr', [8629]], ['cross', [10007]], ['Cross', [10799]], ['Cscr', [119966]], ['cscr', [119992]], ['csub', [10959]], ['csube', [10961]], ['csup', [10960]], ['csupe', [10962]], ['ctdot', [8943]], ['cudarrl', [10552]], ['cudarrr', [10549]], ['cuepr', [8926]], ['cuesc', [8927]], ['cularr', [8630]], ['cularrp', [10557]], ['cupbrcap', [10824]], ['cupcap', [10822]], ['CupCap', [8781]], ['cup', [8746]], ['Cup', [8915]], ['cupcup', [10826]], ['cupdot', [8845]], ['cupor', [10821]], ['cups', [8746, 65024]], ['curarr', [8631]], ['curarrm', [10556]], ['curlyeqprec', [8926]], ['curlyeqsucc', [8927]], ['curlyvee', [8910]], ['curlywedge', [8911]], ['curren', [164]], ['curvearrowleft', [8630]], ['curvearrowright', [8631]], ['cuvee', [8910]], ['cuwed', [8911]], ['cwconint', [8754]], ['cwint', [8753]], ['cylcty', [9005]], ['dagger', [8224]], ['Dagger', [8225]], ['daleth', [8504]], ['darr', [8595]], ['Darr', [8609]], ['dArr', [8659]], ['dash', [8208]], ['Dashv', [10980]], ['dashv', [8867]], ['dbkarow', [10511]], ['dblac', [733]], ['Dcaron', [270]], ['dcaron', [271]], ['Dcy', [1044]], ['dcy', [1076]], ['ddagger', [8225]], ['ddarr', [8650]], ['DD', [8517]], ['dd', [8518]], ['DDotrahd', [10513]], ['ddotseq', [10871]], ['deg', [176]], ['Del', [8711]], ['Delta', [916]], ['delta', [948]], ['demptyv', [10673]], ['dfisht', [10623]], ['Dfr', [120071]], ['dfr', [120097]], ['dHar', [10597]], ['dharl', [8643]], ['dharr', [8642]], ['DiacriticalAcute', [180]], ['DiacriticalDot', [729]], ['DiacriticalDoubleAcute', [733]], ['DiacriticalGrave', [96]], ['DiacriticalTilde', [732]], ['diam', [8900]], ['diamond', [8900]], ['Diamond', [8900]], ['diamondsuit', [9830]], ['diams', [9830]], ['die', [168]], ['DifferentialD', [8518]], ['digamma', [989]], ['disin', [8946]], ['div', [247]], ['divide', [247]], ['divideontimes', [8903]], ['divonx', [8903]], ['DJcy', [1026]], ['djcy', [1106]], ['dlcorn', [8990]], ['dlcrop', [8973]], ['dollar', [36]], ['Dopf', [120123]], ['dopf', [120149]], ['Dot', [168]], ['dot', [729]], ['DotDot', [8412]], ['doteq', [8784]], ['doteqdot', [8785]], ['DotEqual', [8784]], ['dotminus', [8760]], ['dotplus', [8724]], ['dotsquare', [8865]], ['doublebarwedge', [8966]], ['DoubleContourIntegral', [8751]], ['DoubleDot', [168]], ['DoubleDownArrow', [8659]], ['DoubleLeftArrow', [8656]], ['DoubleLeftRightArrow', [8660]], ['DoubleLeftTee', [10980]], ['DoubleLongLeftArrow', [10232]], ['DoubleLongLeftRightArrow', [10234]], ['DoubleLongRightArrow', [10233]], ['DoubleRightArrow', [8658]], ['DoubleRightTee', [8872]], ['DoubleUpArrow', [8657]], ['DoubleUpDownArrow', [8661]], ['DoubleVerticalBar', [8741]], ['DownArrowBar', [10515]], ['downarrow', [8595]], ['DownArrow', [8595]], ['Downarrow', [8659]], ['DownArrowUpArrow', [8693]], ['DownBreve', [785]], ['downdownarrows', [8650]], ['downharpoonleft', [8643]], ['downharpoonright', [8642]], ['DownLeftRightVector', [10576]], ['DownLeftTeeVector', [10590]], ['DownLeftVectorBar', [10582]], ['DownLeftVector', [8637]], ['DownRightTeeVector', [10591]], ['DownRightVectorBar', [10583]], ['DownRightVector', [8641]], ['DownTeeArrow', [8615]], ['DownTee', [8868]], ['drbkarow', [10512]], ['drcorn', [8991]], ['drcrop', [8972]], ['Dscr', [119967]], ['dscr', [119993]], ['DScy', [1029]], ['dscy', [1109]], ['dsol', [10742]], ['Dstrok', [272]], ['dstrok', [273]], ['dtdot', [8945]], ['dtri', [9663]], ['dtrif', [9662]], ['duarr', [8693]], ['duhar', [10607]], ['dwangle', [10662]], ['DZcy', [1039]], ['dzcy', [1119]], ['dzigrarr', [10239]], ['Eacute', [201]], ['eacute', [233]], ['easter', [10862]], ['Ecaron', [282]], ['ecaron', [283]], ['Ecirc', [202]], ['ecirc', [234]], ['ecir', [8790]], ['ecolon', [8789]], ['Ecy', [1069]], ['ecy', [1101]], ['eDDot', [10871]], ['Edot', [278]], ['edot', [279]], ['eDot', [8785]], ['ee', [8519]], ['efDot', [8786]], ['Efr', [120072]], ['efr', [120098]], ['eg', [10906]], ['Egrave', [200]], ['egrave', [232]], ['egs', [10902]], ['egsdot', [10904]], ['el', [10905]], ['Element', [8712]], ['elinters', [9191]], ['ell', [8467]], ['els', [10901]], ['elsdot', [10903]], ['Emacr', [274]], ['emacr', [275]], ['empty', [8709]], ['emptyset', [8709]], ['EmptySmallSquare', [9723]], ['emptyv', [8709]], ['EmptyVerySmallSquare', [9643]], ['emsp13', [8196]], ['emsp14', [8197]], ['emsp', [8195]], ['ENG', [330]], ['eng', [331]], ['ensp', [8194]], ['Eogon', [280]], ['eogon', [281]], ['Eopf', [120124]], ['eopf', [120150]], ['epar', [8917]], ['eparsl', [10723]], ['eplus', [10865]], ['epsi', [949]], ['Epsilon', [917]], ['epsilon', [949]], ['epsiv', [1013]], ['eqcirc', [8790]], ['eqcolon', [8789]], ['eqsim', [8770]], ['eqslantgtr', [10902]], ['eqslantless', [10901]], ['Equal', [10869]], ['equals', [61]], ['EqualTilde', [8770]], ['equest', [8799]], ['Equilibrium', [8652]], ['equiv', [8801]], ['equivDD', [10872]], ['eqvparsl', [10725]], ['erarr', [10609]], ['erDot', [8787]], ['escr', [8495]], ['Escr', [8496]], ['esdot', [8784]], ['Esim', [10867]], ['esim', [8770]], ['Eta', [919]], ['eta', [951]], ['ETH', [208]], ['eth', [240]], ['Euml', [203]], ['euml', [235]], ['euro', [8364]], ['excl', [33]], ['exist', [8707]], ['Exists', [8707]], ['expectation', [8496]], ['exponentiale', [8519]], ['ExponentialE', [8519]], ['fallingdotseq', [8786]], ['Fcy', [1060]], ['fcy', [1092]], ['female', [9792]], ['ffilig', [64259]], ['fflig', [64256]], ['ffllig', [64260]], ['Ffr', [120073]], ['ffr', [120099]], ['filig', [64257]], ['FilledSmallSquare', [9724]], ['FilledVerySmallSquare', [9642]], ['fjlig', [102, 106]], ['flat', [9837]], ['fllig', [64258]], ['fltns', [9649]], ['fnof', [402]], ['Fopf', [120125]], ['fopf', [120151]], ['forall', [8704]], ['ForAll', [8704]], ['fork', [8916]], ['forkv', [10969]], ['Fouriertrf', [8497]], ['fpartint', [10765]], ['frac12', [189]], ['frac13', [8531]], ['frac14', [188]], ['frac15', [8533]], ['frac16', [8537]], ['frac18', [8539]], ['frac23', [8532]], ['frac25', [8534]], ['frac34', [190]], ['frac35', [8535]], ['frac38', [8540]], ['frac45', [8536]], ['frac56', [8538]], ['frac58', [8541]], ['frac78', [8542]], ['frasl', [8260]], ['frown', [8994]], ['fscr', [119995]], ['Fscr', [8497]], ['gacute', [501]], ['Gamma', [915]], ['gamma', [947]], ['Gammad', [988]], ['gammad', [989]], ['gap', [10886]], ['Gbreve', [286]], ['gbreve', [287]], ['Gcedil', [290]], ['Gcirc', [284]], ['gcirc', [285]], ['Gcy', [1043]], ['gcy', [1075]], ['Gdot', [288]], ['gdot', [289]], ['ge', [8805]], ['gE', [8807]], ['gEl', [10892]], ['gel', [8923]], ['geq', [8805]], ['geqq', [8807]], ['geqslant', [10878]], ['gescc', [10921]], ['ges', [10878]], ['gesdot', [10880]], ['gesdoto', [10882]], ['gesdotol', [10884]], ['gesl', [8923, 65024]], ['gesles', [10900]], ['Gfr', [120074]], ['gfr', [120100]], ['gg', [8811]], ['Gg', [8921]], ['ggg', [8921]], ['gimel', [8503]], ['GJcy', [1027]], ['gjcy', [1107]], ['gla', [10917]], ['gl', [8823]], ['glE', [10898]], ['glj', [10916]], ['gnap', [10890]], ['gnapprox', [10890]], ['gne', [10888]], ['gnE', [8809]], ['gneq', [10888]], ['gneqq', [8809]], ['gnsim', [8935]], ['Gopf', [120126]], ['gopf', [120152]], ['grave', [96]], ['GreaterEqual', [8805]], ['GreaterEqualLess', [8923]], ['GreaterFullEqual', [8807]], ['GreaterGreater', [10914]], ['GreaterLess', [8823]], ['GreaterSlantEqual', [10878]], ['GreaterTilde', [8819]], ['Gscr', [119970]], ['gscr', [8458]], ['gsim', [8819]], ['gsime', [10894]], ['gsiml', [10896]], ['gtcc', [10919]], ['gtcir', [10874]], ['gt', [62]], ['GT', [62]], ['Gt', [8811]], ['gtdot', [8919]], ['gtlPar', [10645]], ['gtquest', [10876]], ['gtrapprox', [10886]], ['gtrarr', [10616]], ['gtrdot', [8919]], ['gtreqless', [8923]], ['gtreqqless', [10892]], ['gtrless', [8823]], ['gtrsim', [8819]], ['gvertneqq', [8809, 65024]], ['gvnE', [8809, 65024]], ['Hacek', [711]], ['hairsp', [8202]], ['half', [189]], ['hamilt', [8459]], ['HARDcy', [1066]], ['hardcy', [1098]], ['harrcir', [10568]], ['harr', [8596]], ['hArr', [8660]], ['harrw', [8621]], ['Hat', [94]], ['hbar', [8463]], ['Hcirc', [292]], ['hcirc', [293]], ['hearts', [9829]], ['heartsuit', [9829]], ['hellip', [8230]], ['hercon', [8889]], ['hfr', [120101]], ['Hfr', [8460]], ['HilbertSpace', [8459]], ['hksearow', [10533]], ['hkswarow', [10534]], ['hoarr', [8703]], ['homtht', [8763]], ['hookleftarrow', [8617]], ['hookrightarrow', [8618]], ['hopf', [120153]], ['Hopf', [8461]], ['horbar', [8213]], ['HorizontalLine', [9472]], ['hscr', [119997]], ['Hscr', [8459]], ['hslash', [8463]], ['Hstrok', [294]], ['hstrok', [295]], ['HumpDownHump', [8782]], ['HumpEqual', [8783]], ['hybull', [8259]], ['hyphen', [8208]], ['Iacute', [205]], ['iacute', [237]], ['ic', [8291]], ['Icirc', [206]], ['icirc', [238]], ['Icy', [1048]], ['icy', [1080]], ['Idot', [304]], ['IEcy', [1045]], ['iecy', [1077]], ['iexcl', [161]], ['iff', [8660]], ['ifr', [120102]], ['Ifr', [8465]], ['Igrave', [204]], ['igrave', [236]], ['ii', [8520]], ['iiiint', [10764]], ['iiint', [8749]], ['iinfin', [10716]], ['iiota', [8489]], ['IJlig', [306]], ['ijlig', [307]], ['Imacr', [298]], ['imacr', [299]], ['image', [8465]], ['ImaginaryI', [8520]], ['imagline', [8464]], ['imagpart', [8465]], ['imath', [305]], ['Im', [8465]], ['imof', [8887]], ['imped', [437]], ['Implies', [8658]], ['incare', [8453]], ['in', [8712]], ['infin', [8734]], ['infintie', [10717]], ['inodot', [305]], ['intcal', [8890]], ['int', [8747]], ['Int', [8748]], ['integers', [8484]], ['Integral', [8747]], ['intercal', [8890]], ['Intersection', [8898]], ['intlarhk', [10775]], ['intprod', [10812]], ['InvisibleComma', [8291]], ['InvisibleTimes', [8290]], ['IOcy', [1025]], ['iocy', [1105]], ['Iogon', [302]], ['iogon', [303]], ['Iopf', [120128]], ['iopf', [120154]], ['Iota', [921]], ['iota', [953]], ['iprod', [10812]], ['iquest', [191]], ['iscr', [119998]], ['Iscr', [8464]], ['isin', [8712]], ['isindot', [8949]], ['isinE', [8953]], ['isins', [8948]], ['isinsv', [8947]], ['isinv', [8712]], ['it', [8290]], ['Itilde', [296]], ['itilde', [297]], ['Iukcy', [1030]], ['iukcy', [1110]], ['Iuml', [207]], ['iuml', [239]], ['Jcirc', [308]], ['jcirc', [309]], ['Jcy', [1049]], ['jcy', [1081]], ['Jfr', [120077]], ['jfr', [120103]], ['jmath', [567]], ['Jopf', [120129]], ['jopf', [120155]], ['Jscr', [119973]], ['jscr', [119999]], ['Jsercy', [1032]], ['jsercy', [1112]], ['Jukcy', [1028]], ['jukcy', [1108]], ['Kappa', [922]], ['kappa', [954]], ['kappav', [1008]], ['Kcedil', [310]], ['kcedil', [311]], ['Kcy', [1050]], ['kcy', [1082]], ['Kfr', [120078]], ['kfr', [120104]], ['kgreen', [312]], ['KHcy', [1061]], ['khcy', [1093]], ['KJcy', [1036]], ['kjcy', [1116]], ['Kopf', [120130]], ['kopf', [120156]], ['Kscr', [119974]], ['kscr', [120000]], ['lAarr', [8666]], ['Lacute', [313]], ['lacute', [314]], ['laemptyv', [10676]], ['lagran', [8466]], ['Lambda', [923]], ['lambda', [955]], ['lang', [10216]], ['Lang', [10218]], ['langd', [10641]], ['langle', [10216]], ['lap', [10885]], ['Laplacetrf', [8466]], ['laquo', [171]], ['larrb', [8676]], ['larrbfs', [10527]], ['larr', [8592]], ['Larr', [8606]], ['lArr', [8656]], ['larrfs', [10525]], ['larrhk', [8617]], ['larrlp', [8619]], ['larrpl', [10553]], ['larrsim', [10611]], ['larrtl', [8610]], ['latail', [10521]], ['lAtail', [10523]], ['lat', [10923]], ['late', [10925]], ['lates', [10925, 65024]], ['lbarr', [10508]], ['lBarr', [10510]], ['lbbrk', [10098]], ['lbrace', [123]], ['lbrack', [91]], ['lbrke', [10635]], ['lbrksld', [10639]], ['lbrkslu', [10637]], ['Lcaron', [317]], ['lcaron', [318]], ['Lcedil', [315]], ['lcedil', [316]], ['lceil', [8968]], ['lcub', [123]], ['Lcy', [1051]], ['lcy', [1083]], ['ldca', [10550]], ['ldquo', [8220]], ['ldquor', [8222]], ['ldrdhar', [10599]], ['ldrushar', [10571]], ['ldsh', [8626]], ['le', [8804]], ['lE', [8806]], ['LeftAngleBracket', [10216]], ['LeftArrowBar', [8676]], ['leftarrow', [8592]], ['LeftArrow', [8592]], ['Leftarrow', [8656]], ['LeftArrowRightArrow', [8646]], ['leftarrowtail', [8610]], ['LeftCeiling', [8968]], ['LeftDoubleBracket', [10214]], ['LeftDownTeeVector', [10593]], ['LeftDownVectorBar', [10585]], ['LeftDownVector', [8643]], ['LeftFloor', [8970]], ['leftharpoondown', [8637]], ['leftharpoonup', [8636]], ['leftleftarrows', [8647]], ['leftrightarrow', [8596]], ['LeftRightArrow', [8596]], ['Leftrightarrow', [8660]], ['leftrightarrows', [8646]], ['leftrightharpoons', [8651]], ['leftrightsquigarrow', [8621]], ['LeftRightVector', [10574]], ['LeftTeeArrow', [8612]], ['LeftTee', [8867]], ['LeftTeeVector', [10586]], ['leftthreetimes', [8907]], ['LeftTriangleBar', [10703]], ['LeftTriangle', [8882]], ['LeftTriangleEqual', [8884]], ['LeftUpDownVector', [10577]], ['LeftUpTeeVector', [10592]], ['LeftUpVectorBar', [10584]], ['LeftUpVector', [8639]], ['LeftVectorBar', [10578]], ['LeftVector', [8636]], ['lEg', [10891]], ['leg', [8922]], ['leq', [8804]], ['leqq', [8806]], ['leqslant', [10877]], ['lescc', [10920]], ['les', [10877]], ['lesdot', [10879]], ['lesdoto', [10881]], ['lesdotor', [10883]], ['lesg', [8922, 65024]], ['lesges', [10899]], ['lessapprox', [10885]], ['lessdot', [8918]], ['lesseqgtr', [8922]], ['lesseqqgtr', [10891]], ['LessEqualGreater', [8922]], ['LessFullEqual', [8806]], ['LessGreater', [8822]], ['lessgtr', [8822]], ['LessLess', [10913]], ['lesssim', [8818]], ['LessSlantEqual', [10877]], ['LessTilde', [8818]], ['lfisht', [10620]], ['lfloor', [8970]], ['Lfr', [120079]], ['lfr', [120105]], ['lg', [8822]], ['lgE', [10897]], ['lHar', [10594]], ['lhard', [8637]], ['lharu', [8636]], ['lharul', [10602]], ['lhblk', [9604]], ['LJcy', [1033]], ['ljcy', [1113]], ['llarr', [8647]], ['ll', [8810]], ['Ll', [8920]], ['llcorner', [8990]], ['Lleftarrow', [8666]], ['llhard', [10603]], ['lltri', [9722]], ['Lmidot', [319]], ['lmidot', [320]], ['lmoustache', [9136]], ['lmoust', [9136]], ['lnap', [10889]], ['lnapprox', [10889]], ['lne', [10887]], ['lnE', [8808]], ['lneq', [10887]], ['lneqq', [8808]], ['lnsim', [8934]], ['loang', [10220]], ['loarr', [8701]], ['lobrk', [10214]], ['longleftarrow', [10229]], ['LongLeftArrow', [10229]], ['Longleftarrow', [10232]], ['longleftrightarrow', [10231]], ['LongLeftRightArrow', [10231]], ['Longleftrightarrow', [10234]], ['longmapsto', [10236]], ['longrightarrow', [10230]], ['LongRightArrow', [10230]], ['Longrightarrow', [10233]], ['looparrowleft', [8619]], ['looparrowright', [8620]], ['lopar', [10629]], ['Lopf', [120131]], ['lopf', [120157]], ['loplus', [10797]], ['lotimes', [10804]], ['lowast', [8727]], ['lowbar', [95]], ['LowerLeftArrow', [8601]], ['LowerRightArrow', [8600]], ['loz', [9674]], ['lozenge', [9674]], ['lozf', [10731]], ['lpar', [40]], ['lparlt', [10643]], ['lrarr', [8646]], ['lrcorner', [8991]], ['lrhar', [8651]], ['lrhard', [10605]], ['lrm', [8206]], ['lrtri', [8895]], ['lsaquo', [8249]], ['lscr', [120001]], ['Lscr', [8466]], ['lsh', [8624]], ['Lsh', [8624]], ['lsim', [8818]], ['lsime', [10893]], ['lsimg', [10895]], ['lsqb', [91]], ['lsquo', [8216]], ['lsquor', [8218]], ['Lstrok', [321]], ['lstrok', [322]], ['ltcc', [10918]], ['ltcir', [10873]], ['lt', [60]], ['LT', [60]], ['Lt', [8810]], ['ltdot', [8918]], ['lthree', [8907]], ['ltimes', [8905]], ['ltlarr', [10614]], ['ltquest', [10875]], ['ltri', [9667]], ['ltrie', [8884]], ['ltrif', [9666]], ['ltrPar', [10646]], ['lurdshar', [10570]], ['luruhar', [10598]], ['lvertneqq', [8808, 65024]], ['lvnE', [8808, 65024]], ['macr', [175]], ['male', [9794]], ['malt', [10016]], ['maltese', [10016]], ['Map', [10501]], ['map', [8614]], ['mapsto', [8614]], ['mapstodown', [8615]], ['mapstoleft', [8612]], ['mapstoup', [8613]], ['marker', [9646]], ['mcomma', [10793]], ['Mcy', [1052]], ['mcy', [1084]], ['mdash', [8212]], ['mDDot', [8762]], ['measuredangle', [8737]], ['MediumSpace', [8287]], ['Mellintrf', [8499]], ['Mfr', [120080]], ['mfr', [120106]], ['mho', [8487]], ['micro', [181]], ['midast', [42]], ['midcir', [10992]], ['mid', [8739]], ['middot', [183]], ['minusb', [8863]], ['minus', [8722]], ['minusd', [8760]], ['minusdu', [10794]], ['MinusPlus', [8723]], ['mlcp', [10971]], ['mldr', [8230]], ['mnplus', [8723]], ['models', [8871]], ['Mopf', [120132]], ['mopf', [120158]], ['mp', [8723]], ['mscr', [120002]], ['Mscr', [8499]], ['mstpos', [8766]], ['Mu', [924]], ['mu', [956]], ['multimap', [8888]], ['mumap', [8888]], ['nabla', [8711]], ['Nacute', [323]], ['nacute', [324]], ['nang', [8736, 8402]], ['nap', [8777]], ['napE', [10864, 824]], ['napid', [8779, 824]], ['napos', [329]], ['napprox', [8777]], ['natural', [9838]], ['naturals', [8469]], ['natur', [9838]], ['nbsp', [160]], ['nbump', [8782, 824]], ['nbumpe', [8783, 824]], ['ncap', [10819]], ['Ncaron', [327]], ['ncaron', [328]], ['Ncedil', [325]], ['ncedil', [326]], ['ncong', [8775]], ['ncongdot', [10861, 824]], ['ncup', [10818]], ['Ncy', [1053]], ['ncy', [1085]], ['ndash', [8211]], ['nearhk', [10532]], ['nearr', [8599]], ['neArr', [8663]], ['nearrow', [8599]], ['ne', [8800]], ['nedot', [8784, 824]], ['NegativeMediumSpace', [8203]], ['NegativeThickSpace', [8203]], ['NegativeThinSpace', [8203]], ['NegativeVeryThinSpace', [8203]], ['nequiv', [8802]], ['nesear', [10536]], ['nesim', [8770, 824]], ['NestedGreaterGreater', [8811]], ['NestedLessLess', [8810]], ['nexist', [8708]], ['nexists', [8708]], ['Nfr', [120081]], ['nfr', [120107]], ['ngE', [8807, 824]], ['nge', [8817]], ['ngeq', [8817]], ['ngeqq', [8807, 824]], ['ngeqslant', [10878, 824]], ['nges', [10878, 824]], ['nGg', [8921, 824]], ['ngsim', [8821]], ['nGt', [8811, 8402]], ['ngt', [8815]], ['ngtr', [8815]], ['nGtv', [8811, 824]], ['nharr', [8622]], ['nhArr', [8654]], ['nhpar', [10994]], ['ni', [8715]], ['nis', [8956]], ['nisd', [8954]], ['niv', [8715]], ['NJcy', [1034]], ['njcy', [1114]], ['nlarr', [8602]], ['nlArr', [8653]], ['nldr', [8229]], ['nlE', [8806, 824]], ['nle', [8816]], ['nleftarrow', [8602]], ['nLeftarrow', [8653]], ['nleftrightarrow', [8622]], ['nLeftrightarrow', [8654]], ['nleq', [8816]], ['nleqq', [8806, 824]], ['nleqslant', [10877, 824]], ['nles', [10877, 824]], ['nless', [8814]], ['nLl', [8920, 824]], ['nlsim', [8820]], ['nLt', [8810, 8402]], ['nlt', [8814]], ['nltri', [8938]], ['nltrie', [8940]], ['nLtv', [8810, 824]], ['nmid', [8740]], ['NoBreak', [8288]], ['NonBreakingSpace', [160]], ['nopf', [120159]], ['Nopf', [8469]], ['Not', [10988]], ['not', [172]], ['NotCongruent', [8802]], ['NotCupCap', [8813]], ['NotDoubleVerticalBar', [8742]], ['NotElement', [8713]], ['NotEqual', [8800]], ['NotEqualTilde', [8770, 824]], ['NotExists', [8708]], ['NotGreater', [8815]], ['NotGreaterEqual', [8817]], ['NotGreaterFullEqual', [8807, 824]], ['NotGreaterGreater', [8811, 824]], ['NotGreaterLess', [8825]], ['NotGreaterSlantEqual', [10878, 824]], ['NotGreaterTilde', [8821]], ['NotHumpDownHump', [8782, 824]], ['NotHumpEqual', [8783, 824]], ['notin', [8713]], ['notindot', [8949, 824]], ['notinE', [8953, 824]], ['notinva', [8713]], ['notinvb', [8951]], ['notinvc', [8950]], ['NotLeftTriangleBar', [10703, 824]], ['NotLeftTriangle', [8938]], ['NotLeftTriangleEqual', [8940]], ['NotLess', [8814]], ['NotLessEqual', [8816]], ['NotLessGreater', [8824]], ['NotLessLess', [8810, 824]], ['NotLessSlantEqual', [10877, 824]], ['NotLessTilde', [8820]], ['NotNestedGreaterGreater', [10914, 824]], ['NotNestedLessLess', [10913, 824]], ['notni', [8716]], ['notniva', [8716]], ['notnivb', [8958]], ['notnivc', [8957]], ['NotPrecedes', [8832]], ['NotPrecedesEqual', [10927, 824]], ['NotPrecedesSlantEqual', [8928]], ['NotReverseElement', [8716]], ['NotRightTriangleBar', [10704, 824]], ['NotRightTriangle', [8939]], ['NotRightTriangleEqual', [8941]], ['NotSquareSubset', [8847, 824]], ['NotSquareSubsetEqual', [8930]], ['NotSquareSuperset', [8848, 824]], ['NotSquareSupersetEqual', [8931]], ['NotSubset', [8834, 8402]], ['NotSubsetEqual', [8840]], ['NotSucceeds', [8833]], ['NotSucceedsEqual', [10928, 824]], ['NotSucceedsSlantEqual', [8929]], ['NotSucceedsTilde', [8831, 824]], ['NotSuperset', [8835, 8402]], ['NotSupersetEqual', [8841]], ['NotTilde', [8769]], ['NotTildeEqual', [8772]], ['NotTildeFullEqual', [8775]], ['NotTildeTilde', [8777]], ['NotVerticalBar', [8740]], ['nparallel', [8742]], ['npar', [8742]], ['nparsl', [11005, 8421]], ['npart', [8706, 824]], ['npolint', [10772]], ['npr', [8832]], ['nprcue', [8928]], ['nprec', [8832]], ['npreceq', [10927, 824]], ['npre', [10927, 824]], ['nrarrc', [10547, 824]], ['nrarr', [8603]], ['nrArr', [8655]], ['nrarrw', [8605, 824]], ['nrightarrow', [8603]], ['nRightarrow', [8655]], ['nrtri', [8939]], ['nrtrie', [8941]], ['nsc', [8833]], ['nsccue', [8929]], ['nsce', [10928, 824]], ['Nscr', [119977]], ['nscr', [120003]], ['nshortmid', [8740]], ['nshortparallel', [8742]], ['nsim', [8769]], ['nsime', [8772]], ['nsimeq', [8772]], ['nsmid', [8740]], ['nspar', [8742]], ['nsqsube', [8930]], ['nsqsupe', [8931]], ['nsub', [8836]], ['nsubE', [10949, 824]], ['nsube', [8840]], ['nsubset', [8834, 8402]], ['nsubseteq', [8840]], ['nsubseteqq', [10949, 824]], ['nsucc', [8833]], ['nsucceq', [10928, 824]], ['nsup', [8837]], ['nsupE', [10950, 824]], ['nsupe', [8841]], ['nsupset', [8835, 8402]], ['nsupseteq', [8841]], ['nsupseteqq', [10950, 824]], ['ntgl', [8825]], ['Ntilde', [209]], ['ntilde', [241]], ['ntlg', [8824]], ['ntriangleleft', [8938]], ['ntrianglelefteq', [8940]], ['ntriangleright', [8939]], ['ntrianglerighteq', [8941]], ['Nu', [925]], ['nu', [957]], ['num', [35]], ['numero', [8470]], ['numsp', [8199]], ['nvap', [8781, 8402]], ['nvdash', [8876]], ['nvDash', [8877]], ['nVdash', [8878]], ['nVDash', [8879]], ['nvge', [8805, 8402]], ['nvgt', [62, 8402]], ['nvHarr', [10500]], ['nvinfin', [10718]], ['nvlArr', [10498]], ['nvle', [8804, 8402]], ['nvlt', [60, 8402]], ['nvltrie', [8884, 8402]], ['nvrArr', [10499]], ['nvrtrie', [8885, 8402]], ['nvsim', [8764, 8402]], ['nwarhk', [10531]], ['nwarr', [8598]], ['nwArr', [8662]], ['nwarrow', [8598]], ['nwnear', [10535]], ['Oacute', [211]], ['oacute', [243]], ['oast', [8859]], ['Ocirc', [212]], ['ocirc', [244]], ['ocir', [8858]], ['Ocy', [1054]], ['ocy', [1086]], ['odash', [8861]], ['Odblac', [336]], ['odblac', [337]], ['odiv', [10808]], ['odot', [8857]], ['odsold', [10684]], ['OElig', [338]], ['oelig', [339]], ['ofcir', [10687]], ['Ofr', [120082]], ['ofr', [120108]], ['ogon', [731]], ['Ograve', [210]], ['ograve', [242]], ['ogt', [10689]], ['ohbar', [10677]], ['ohm', [937]], ['oint', [8750]], ['olarr', [8634]], ['olcir', [10686]], ['olcross', [10683]], ['oline', [8254]], ['olt', [10688]], ['Omacr', [332]], ['omacr', [333]], ['Omega', [937]], ['omega', [969]], ['Omicron', [927]], ['omicron', [959]], ['omid', [10678]], ['ominus', [8854]], ['Oopf', [120134]], ['oopf', [120160]], ['opar', [10679]], ['OpenCurlyDoubleQuote', [8220]], ['OpenCurlyQuote', [8216]], ['operp', [10681]], ['oplus', [8853]], ['orarr', [8635]], ['Or', [10836]], ['or', [8744]], ['ord', [10845]], ['order', [8500]], ['orderof', [8500]], ['ordf', [170]], ['ordm', [186]], ['origof', [8886]], ['oror', [10838]], ['orslope', [10839]], ['orv', [10843]], ['oS', [9416]], ['Oscr', [119978]], ['oscr', [8500]], ['Oslash', [216]], ['oslash', [248]], ['osol', [8856]], ['Otilde', [213]], ['otilde', [245]], ['otimesas', [10806]], ['Otimes', [10807]], ['otimes', [8855]], ['Ouml', [214]], ['ouml', [246]], ['ovbar', [9021]], ['OverBar', [8254]], ['OverBrace', [9182]], ['OverBracket', [9140]], ['OverParenthesis', [9180]], ['para', [182]], ['parallel', [8741]], ['par', [8741]], ['parsim', [10995]], ['parsl', [11005]], ['part', [8706]], ['PartialD', [8706]], ['Pcy', [1055]], ['pcy', [1087]], ['percnt', [37]], ['period', [46]], ['permil', [8240]], ['perp', [8869]], ['pertenk', [8241]], ['Pfr', [120083]], ['pfr', [120109]], ['Phi', [934]], ['phi', [966]], ['phiv', [981]], ['phmmat', [8499]], ['phone', [9742]], ['Pi', [928]], ['pi', [960]], ['pitchfork', [8916]], ['piv', [982]], ['planck', [8463]], ['planckh', [8462]], ['plankv', [8463]], ['plusacir', [10787]], ['plusb', [8862]], ['pluscir', [10786]], ['plus', [43]], ['plusdo', [8724]], ['plusdu', [10789]], ['pluse', [10866]], ['PlusMinus', [177]], ['plusmn', [177]], ['plussim', [10790]], ['plustwo', [10791]], ['pm', [177]], ['Poincareplane', [8460]], ['pointint', [10773]], ['popf', [120161]], ['Popf', [8473]], ['pound', [163]], ['prap', [10935]], ['Pr', [10939]], ['pr', [8826]], ['prcue', [8828]], ['precapprox', [10935]], ['prec', [8826]], ['preccurlyeq', [8828]], ['Precedes', [8826]], ['PrecedesEqual', [10927]], ['PrecedesSlantEqual', [8828]], ['PrecedesTilde', [8830]], ['preceq', [10927]], ['precnapprox', [10937]], ['precneqq', [10933]], ['precnsim', [8936]], ['pre', [10927]], ['prE', [10931]], ['precsim', [8830]], ['prime', [8242]], ['Prime', [8243]], ['primes', [8473]], ['prnap', [10937]], ['prnE', [10933]], ['prnsim', [8936]], ['prod', [8719]], ['Product', [8719]], ['profalar', [9006]], ['profline', [8978]], ['profsurf', [8979]], ['prop', [8733]], ['Proportional', [8733]], ['Proportion', [8759]], ['propto', [8733]], ['prsim', [8830]], ['prurel', [8880]], ['Pscr', [119979]], ['pscr', [120005]], ['Psi', [936]], ['psi', [968]], ['puncsp', [8200]], ['Qfr', [120084]], ['qfr', [120110]], ['qint', [10764]], ['qopf', [120162]], ['Qopf', [8474]], ['qprime', [8279]], ['Qscr', [119980]], ['qscr', [120006]], ['quaternions', [8461]], ['quatint', [10774]], ['quest', [63]], ['questeq', [8799]], ['quot', [34]], ['QUOT', [34]], ['rAarr', [8667]], ['race', [8765, 817]], ['Racute', [340]], ['racute', [341]], ['radic', [8730]], ['raemptyv', [10675]], ['rang', [10217]], ['Rang', [10219]], ['rangd', [10642]], ['range', [10661]], ['rangle', [10217]], ['raquo', [187]], ['rarrap', [10613]], ['rarrb', [8677]], ['rarrbfs', [10528]], ['rarrc', [10547]], ['rarr', [8594]], ['Rarr', [8608]], ['rArr', [8658]], ['rarrfs', [10526]], ['rarrhk', [8618]], ['rarrlp', [8620]], ['rarrpl', [10565]], ['rarrsim', [10612]], ['Rarrtl', [10518]], ['rarrtl', [8611]], ['rarrw', [8605]], ['ratail', [10522]], ['rAtail', [10524]], ['ratio', [8758]], ['rationals', [8474]], ['rbarr', [10509]], ['rBarr', [10511]], ['RBarr', [10512]], ['rbbrk', [10099]], ['rbrace', [125]], ['rbrack', [93]], ['rbrke', [10636]], ['rbrksld', [10638]], ['rbrkslu', [10640]], ['Rcaron', [344]], ['rcaron', [345]], ['Rcedil', [342]], ['rcedil', [343]], ['rceil', [8969]], ['rcub', [125]], ['Rcy', [1056]], ['rcy', [1088]], ['rdca', [10551]], ['rdldhar', [10601]], ['rdquo', [8221]], ['rdquor', [8221]], ['CloseCurlyDoubleQuote', [8221]], ['rdsh', [8627]], ['real', [8476]], ['realine', [8475]], ['realpart', [8476]], ['reals', [8477]], ['Re', [8476]], ['rect', [9645]], ['reg', [174]], ['REG', [174]], ['ReverseElement', [8715]], ['ReverseEquilibrium', [8651]], ['ReverseUpEquilibrium', [10607]], ['rfisht', [10621]], ['rfloor', [8971]], ['rfr', [120111]], ['Rfr', [8476]], ['rHar', [10596]], ['rhard', [8641]], ['rharu', [8640]], ['rharul', [10604]], ['Rho', [929]], ['rho', [961]], ['rhov', [1009]], ['RightAngleBracket', [10217]], ['RightArrowBar', [8677]], ['rightarrow', [8594]], ['RightArrow', [8594]], ['Rightarrow', [8658]], ['RightArrowLeftArrow', [8644]], ['rightarrowtail', [8611]], ['RightCeiling', [8969]], ['RightDoubleBracket', [10215]], ['RightDownTeeVector', [10589]], ['RightDownVectorBar', [10581]], ['RightDownVector', [8642]], ['RightFloor', [8971]], ['rightharpoondown', [8641]], ['rightharpoonup', [8640]], ['rightleftarrows', [8644]], ['rightleftharpoons', [8652]], ['rightrightarrows', [8649]], ['rightsquigarrow', [8605]], ['RightTeeArrow', [8614]], ['RightTee', [8866]], ['RightTeeVector', [10587]], ['rightthreetimes', [8908]], ['RightTriangleBar', [10704]], ['RightTriangle', [8883]], ['RightTriangleEqual', [8885]], ['RightUpDownVector', [10575]], ['RightUpTeeVector', [10588]], ['RightUpVectorBar', [10580]], ['RightUpVector', [8638]], ['RightVectorBar', [10579]], ['RightVector', [8640]], ['ring', [730]], ['risingdotseq', [8787]], ['rlarr', [8644]], ['rlhar', [8652]], ['rlm', [8207]], ['rmoustache', [9137]], ['rmoust', [9137]], ['rnmid', [10990]], ['roang', [10221]], ['roarr', [8702]], ['robrk', [10215]], ['ropar', [10630]], ['ropf', [120163]], ['Ropf', [8477]], ['roplus', [10798]], ['rotimes', [10805]], ['RoundImplies', [10608]], ['rpar', [41]], ['rpargt', [10644]], ['rppolint', [10770]], ['rrarr', [8649]], ['Rrightarrow', [8667]], ['rsaquo', [8250]], ['rscr', [120007]], ['Rscr', [8475]], ['rsh', [8625]], ['Rsh', [8625]], ['rsqb', [93]], ['rsquo', [8217]], ['rsquor', [8217]], ['CloseCurlyQuote', [8217]], ['rthree', [8908]], ['rtimes', [8906]], ['rtri', [9657]], ['rtrie', [8885]], ['rtrif', [9656]], ['rtriltri', [10702]], ['RuleDelayed', [10740]], ['ruluhar', [10600]], ['rx', [8478]], ['Sacute', [346]], ['sacute', [347]], ['sbquo', [8218]], ['scap', [10936]], ['Scaron', [352]], ['scaron', [353]], ['Sc', [10940]], ['sc', [8827]], ['sccue', [8829]], ['sce', [10928]], ['scE', [10932]], ['Scedil', [350]], ['scedil', [351]], ['Scirc', [348]], ['scirc', [349]], ['scnap', [10938]], ['scnE', [10934]], ['scnsim', [8937]], ['scpolint', [10771]], ['scsim', [8831]], ['Scy', [1057]], ['scy', [1089]], ['sdotb', [8865]], ['sdot', [8901]], ['sdote', [10854]], ['searhk', [10533]], ['searr', [8600]], ['seArr', [8664]], ['searrow', [8600]], ['sect', [167]], ['semi', [59]], ['seswar', [10537]], ['setminus', [8726]], ['setmn', [8726]], ['sext', [10038]], ['Sfr', [120086]], ['sfr', [120112]], ['sfrown', [8994]], ['sharp', [9839]], ['SHCHcy', [1065]], ['shchcy', [1097]], ['SHcy', [1064]], ['shcy', [1096]], ['ShortDownArrow', [8595]], ['ShortLeftArrow', [8592]], ['shortmid', [8739]], ['shortparallel', [8741]], ['ShortRightArrow', [8594]], ['ShortUpArrow', [8593]], ['shy', [173]], ['Sigma', [931]], ['sigma', [963]], ['sigmaf', [962]], ['sigmav', [962]], ['sim', [8764]], ['simdot', [10858]], ['sime', [8771]], ['simeq', [8771]], ['simg', [10910]], ['simgE', [10912]], ['siml', [10909]], ['simlE', [10911]], ['simne', [8774]], ['simplus', [10788]], ['simrarr', [10610]], ['slarr', [8592]], ['SmallCircle', [8728]], ['smallsetminus', [8726]], ['smashp', [10803]], ['smeparsl', [10724]], ['smid', [8739]], ['smile', [8995]], ['smt', [10922]], ['smte', [10924]], ['smtes', [10924, 65024]], ['SOFTcy', [1068]], ['softcy', [1100]], ['solbar', [9023]], ['solb', [10692]], ['sol', [47]], ['Sopf', [120138]], ['sopf', [120164]], ['spades', [9824]], ['spadesuit', [9824]], ['spar', [8741]], ['sqcap', [8851]], ['sqcaps', [8851, 65024]], ['sqcup', [8852]], ['sqcups', [8852, 65024]], ['Sqrt', [8730]], ['sqsub', [8847]], ['sqsube', [8849]], ['sqsubset', [8847]], ['sqsubseteq', [8849]], ['sqsup', [8848]], ['sqsupe', [8850]], ['sqsupset', [8848]], ['sqsupseteq', [8850]], ['square', [9633]], ['Square', [9633]], ['SquareIntersection', [8851]], ['SquareSubset', [8847]], ['SquareSubsetEqual', [8849]], ['SquareSuperset', [8848]], ['SquareSupersetEqual', [8850]], ['SquareUnion', [8852]], ['squarf', [9642]], ['squ', [9633]], ['squf', [9642]], ['srarr', [8594]], ['Sscr', [119982]], ['sscr', [120008]], ['ssetmn', [8726]], ['ssmile', [8995]], ['sstarf', [8902]], ['Star', [8902]], ['star', [9734]], ['starf', [9733]], ['straightepsilon', [1013]], ['straightphi', [981]], ['strns', [175]], ['sub', [8834]], ['Sub', [8912]], ['subdot', [10941]], ['subE', [10949]], ['sube', [8838]], ['subedot', [10947]], ['submult', [10945]], ['subnE', [10955]], ['subne', [8842]], ['subplus', [10943]], ['subrarr', [10617]], ['subset', [8834]], ['Subset', [8912]], ['subseteq', [8838]], ['subseteqq', [10949]], ['SubsetEqual', [8838]], ['subsetneq', [8842]], ['subsetneqq', [10955]], ['subsim', [10951]], ['subsub', [10965]], ['subsup', [10963]], ['succapprox', [10936]], ['succ', [8827]], ['succcurlyeq', [8829]], ['Succeeds', [8827]], ['SucceedsEqual', [10928]], ['SucceedsSlantEqual', [8829]], ['SucceedsTilde', [8831]], ['succeq', [10928]], ['succnapprox', [10938]], ['succneqq', [10934]], ['succnsim', [8937]], ['succsim', [8831]], ['SuchThat', [8715]], ['sum', [8721]], ['Sum', [8721]], ['sung', [9834]], ['sup1', [185]], ['sup2', [178]], ['sup3', [179]], ['sup', [8835]], ['Sup', [8913]], ['supdot', [10942]], ['supdsub', [10968]], ['supE', [10950]], ['supe', [8839]], ['supedot', [10948]], ['Superset', [8835]], ['SupersetEqual', [8839]], ['suphsol', [10185]], ['suphsub', [10967]], ['suplarr', [10619]], ['supmult', [10946]], ['supnE', [10956]], ['supne', [8843]], ['supplus', [10944]], ['supset', [8835]], ['Supset', [8913]], ['supseteq', [8839]], ['supseteqq', [10950]], ['supsetneq', [8843]], ['supsetneqq', [10956]], ['supsim', [10952]], ['supsub', [10964]], ['supsup', [10966]], ['swarhk', [10534]], ['swarr', [8601]], ['swArr', [8665]], ['swarrow', [8601]], ['swnwar', [10538]], ['szlig', [223]], ['Tab', [9]], ['target', [8982]], ['Tau', [932]], ['tau', [964]], ['tbrk', [9140]], ['Tcaron', [356]], ['tcaron', [357]], ['Tcedil', [354]], ['tcedil', [355]], ['Tcy', [1058]], ['tcy', [1090]], ['tdot', [8411]], ['telrec', [8981]], ['Tfr', [120087]], ['tfr', [120113]], ['there4', [8756]], ['therefore', [8756]], ['Therefore', [8756]], ['Theta', [920]], ['theta', [952]], ['thetasym', [977]], ['thetav', [977]], ['thickapprox', [8776]], ['thicksim', [8764]], ['ThickSpace', [8287, 8202]], ['ThinSpace', [8201]], ['thinsp', [8201]], ['thkap', [8776]], ['thksim', [8764]], ['THORN', [222]], ['thorn', [254]], ['tilde', [732]], ['Tilde', [8764]], ['TildeEqual', [8771]], ['TildeFullEqual', [8773]], ['TildeTilde', [8776]], ['timesbar', [10801]], ['timesb', [8864]], ['times', [215]], ['timesd', [10800]], ['tint', [8749]], ['toea', [10536]], ['topbot', [9014]], ['topcir', [10993]], ['top', [8868]], ['Topf', [120139]], ['topf', [120165]], ['topfork', [10970]], ['tosa', [10537]], ['tprime', [8244]], ['trade', [8482]], ['TRADE', [8482]], ['triangle', [9653]], ['triangledown', [9663]], ['triangleleft', [9667]], ['trianglelefteq', [8884]], ['triangleq', [8796]], ['triangleright', [9657]], ['trianglerighteq', [8885]], ['tridot', [9708]], ['trie', [8796]], ['triminus', [10810]], ['TripleDot', [8411]], ['triplus', [10809]], ['trisb', [10701]], ['tritime', [10811]], ['trpezium', [9186]], ['Tscr', [119983]], ['tscr', [120009]], ['TScy', [1062]], ['tscy', [1094]], ['TSHcy', [1035]], ['tshcy', [1115]], ['Tstrok', [358]], ['tstrok', [359]], ['twixt', [8812]], ['twoheadleftarrow', [8606]], ['twoheadrightarrow', [8608]], ['Uacute', [218]], ['uacute', [250]], ['uarr', [8593]], ['Uarr', [8607]], ['uArr', [8657]], ['Uarrocir', [10569]], ['Ubrcy', [1038]], ['ubrcy', [1118]], ['Ubreve', [364]], ['ubreve', [365]], ['Ucirc', [219]], ['ucirc', [251]], ['Ucy', [1059]], ['ucy', [1091]], ['udarr', [8645]], ['Udblac', [368]], ['udblac', [369]], ['udhar', [10606]], ['ufisht', [10622]], ['Ufr', [120088]], ['ufr', [120114]], ['Ugrave', [217]], ['ugrave', [249]], ['uHar', [10595]], ['uharl', [8639]], ['uharr', [8638]], ['uhblk', [9600]], ['ulcorn', [8988]], ['ulcorner', [8988]], ['ulcrop', [8975]], ['ultri', [9720]], ['Umacr', [362]], ['umacr', [363]], ['uml', [168]], ['UnderBar', [95]], ['UnderBrace', [9183]], ['UnderBracket', [9141]], ['UnderParenthesis', [9181]], ['Union', [8899]], ['UnionPlus', [8846]], ['Uogon', [370]], ['uogon', [371]], ['Uopf', [120140]], ['uopf', [120166]], ['UpArrowBar', [10514]], ['uparrow', [8593]], ['UpArrow', [8593]], ['Uparrow', [8657]], ['UpArrowDownArrow', [8645]], ['updownarrow', [8597]], ['UpDownArrow', [8597]], ['Updownarrow', [8661]], ['UpEquilibrium', [10606]], ['upharpoonleft', [8639]], ['upharpoonright', [8638]], ['uplus', [8846]], ['UpperLeftArrow', [8598]], ['UpperRightArrow', [8599]], ['upsi', [965]], ['Upsi', [978]], ['upsih', [978]], ['Upsilon', [933]], ['upsilon', [965]], ['UpTeeArrow', [8613]], ['UpTee', [8869]], ['upuparrows', [8648]], ['urcorn', [8989]], ['urcorner', [8989]], ['urcrop', [8974]], ['Uring', [366]], ['uring', [367]], ['urtri', [9721]], ['Uscr', [119984]], ['uscr', [120010]], ['utdot', [8944]], ['Utilde', [360]], ['utilde', [361]], ['utri', [9653]], ['utrif', [9652]], ['uuarr', [8648]], ['Uuml', [220]], ['uuml', [252]], ['uwangle', [10663]], ['vangrt', [10652]], ['varepsilon', [1013]], ['varkappa', [1008]], ['varnothing', [8709]], ['varphi', [981]], ['varpi', [982]], ['varpropto', [8733]], ['varr', [8597]], ['vArr', [8661]], ['varrho', [1009]], ['varsigma', [962]], ['varsubsetneq', [8842, 65024]], ['varsubsetneqq', [10955, 65024]], ['varsupsetneq', [8843, 65024]], ['varsupsetneqq', [10956, 65024]], ['vartheta', [977]], ['vartriangleleft', [8882]], ['vartriangleright', [8883]], ['vBar', [10984]], ['Vbar', [10987]], ['vBarv', [10985]], ['Vcy', [1042]], ['vcy', [1074]], ['vdash', [8866]], ['vDash', [8872]], ['Vdash', [8873]], ['VDash', [8875]], ['Vdashl', [10982]], ['veebar', [8891]], ['vee', [8744]], ['Vee', [8897]], ['veeeq', [8794]], ['vellip', [8942]], ['verbar', [124]], ['Verbar', [8214]], ['vert', [124]], ['Vert', [8214]], ['VerticalBar', [8739]], ['VerticalLine', [124]], ['VerticalSeparator', [10072]], ['VerticalTilde', [8768]], ['VeryThinSpace', [8202]], ['Vfr', [120089]], ['vfr', [120115]], ['vltri', [8882]], ['vnsub', [8834, 8402]], ['vnsup', [8835, 8402]], ['Vopf', [120141]], ['vopf', [120167]], ['vprop', [8733]], ['vrtri', [8883]], ['Vscr', [119985]], ['vscr', [120011]], ['vsubnE', [10955, 65024]], ['vsubne', [8842, 65024]], ['vsupnE', [10956, 65024]], ['vsupne', [8843, 65024]], ['Vvdash', [8874]], ['vzigzag', [10650]], ['Wcirc', [372]], ['wcirc', [373]], ['wedbar', [10847]], ['wedge', [8743]], ['Wedge', [8896]], ['wedgeq', [8793]], ['weierp', [8472]], ['Wfr', [120090]], ['wfr', [120116]], ['Wopf', [120142]], ['wopf', [120168]], ['wp', [8472]], ['wr', [8768]], ['wreath', [8768]], ['Wscr', [119986]], ['wscr', [120012]], ['xcap', [8898]], ['xcirc', [9711]], ['xcup', [8899]], ['xdtri', [9661]], ['Xfr', [120091]], ['xfr', [120117]], ['xharr', [10231]], ['xhArr', [10234]], ['Xi', [926]], ['xi', [958]], ['xlarr', [10229]], ['xlArr', [10232]], ['xmap', [10236]], ['xnis', [8955]], ['xodot', [10752]], ['Xopf', [120143]], ['xopf', [120169]], ['xoplus', [10753]], ['xotime', [10754]], ['xrarr', [10230]], ['xrArr', [10233]], ['Xscr', [119987]], ['xscr', [120013]], ['xsqcup', [10758]], ['xuplus', [10756]], ['xutri', [9651]], ['xvee', [8897]], ['xwedge', [8896]], ['Yacute', [221]], ['yacute', [253]], ['YAcy', [1071]], ['yacy', [1103]], ['Ycirc', [374]], ['ycirc', [375]], ['Ycy', [1067]], ['ycy', [1099]], ['yen', [165]], ['Yfr', [120092]], ['yfr', [120118]], ['YIcy', [1031]], ['yicy', [1111]], ['Yopf', [120144]], ['yopf', [120170]], ['Yscr', [119988]], ['yscr', [120014]], ['YUcy', [1070]], ['yucy', [1102]], ['yuml', [255]], ['Yuml', [376]], ['Zacute', [377]], ['zacute', [378]], ['Zcaron', [381]], ['zcaron', [382]], ['Zcy', [1047]], ['zcy', [1079]], ['Zdot', [379]], ['zdot', [380]], ['zeetrf', [8488]], ['ZeroWidthSpace', [8203]], ['Zeta', [918]], ['zeta', [950]], ['zfr', [120119]], ['Zfr', [8488]], ['ZHcy', [1046]], ['zhcy', [1078]], ['zigrarr', [8669]], ['zopf', [120171]], ['Zopf', [8484]], ['Zscr', [119989]], ['zscr', [120015]], ['zwj', [8205]], ['zwnj', [8204]]]; 4 | const DECODE_ONLY_ENTITIES: [string, number[]][] = [['NewLine', [10]]]; 5 | 6 | const alphaIndex: {[k: string]: string} = {}; 7 | const charIndex: {[k: string]: {[k: string]: string}} = {}; 8 | 9 | createIndexes(alphaIndex, charIndex); 10 | 11 | export class Html5Entities { 12 | decode(str: string) { 13 | if (!str || !str.length) { 14 | return ''; 15 | } 16 | return str.replace(/&(#?[\w\d]+);?/g, function(s, entity: string) { 17 | let chr; 18 | if (entity.charAt(0) === "#") { 19 | const code = entity.charAt(1) === 'x' ? 20 | parseInt(entity.substr(2).toLowerCase(), 16) : 21 | parseInt(entity.substr(1)); 22 | 23 | if (!isNaN(code) || code >= -32768) { 24 | if (code <= 65535) { 25 | chr = String.fromCharCode(code); 26 | } else { 27 | chr = fromCodePoint(code); 28 | } 29 | } 30 | } else { 31 | chr = alphaIndex[entity]; 32 | } 33 | return chr || s; 34 | }); 35 | } 36 | 37 | static decode(str: string) { 38 | return new Html5Entities().decode(str); 39 | } 40 | 41 | encode(str: string) { 42 | if (!str || !str.length) { 43 | return ''; 44 | } 45 | const strLength = str.length; 46 | let result = ''; 47 | let i = 0; 48 | while (i < strLength) { 49 | const charInfo = charIndex[str.charCodeAt(i)]; 50 | if (charInfo) { 51 | let alpha = charInfo[str.charCodeAt(i + 1)]; 52 | if (alpha) { 53 | i++; 54 | } else { 55 | alpha = charInfo['']; 56 | } 57 | if (alpha) { 58 | result += "&" + alpha + ";"; 59 | i++; 60 | continue; 61 | } 62 | } 63 | result += str.charAt(i); 64 | i++; 65 | } 66 | return result; 67 | } 68 | 69 | static encode(str: string) { 70 | return new Html5Entities().encode(str); 71 | } 72 | 73 | encodeNonUTF(str: string) { 74 | if (!str || !str.length) { 75 | return ''; 76 | } 77 | const strLength = str.length; 78 | let result = ''; 79 | let i = 0; 80 | while (i < strLength) { 81 | const c = str.charCodeAt(i); 82 | const charInfo = charIndex[c]; 83 | if (charInfo) { 84 | let alpha = charInfo[str.charCodeAt(i + 1)]; 85 | if (alpha) { 86 | i++; 87 | } else { 88 | alpha = charInfo['']; 89 | } 90 | if (alpha) { 91 | result += "&" + alpha + ";"; 92 | i++; 93 | continue; 94 | } 95 | } 96 | if (c < 32 || c > 126) { 97 | if (c >= highSurrogateFrom && c <= highSurrogateTo) { 98 | result += '&#' + getCodePoint(str, i) + ';'; 99 | i++; 100 | } else { 101 | result += '&#' + c + ';'; 102 | } 103 | } else { 104 | result += str.charAt(i); 105 | } 106 | i++; 107 | } 108 | return result; 109 | } 110 | 111 | static encodeNonUTF(str: string) { 112 | return new Html5Entities().encodeNonUTF(str); 113 | } 114 | 115 | encodeNonASCII(str: string) { 116 | if (!str || !str.length) { 117 | return ''; 118 | } 119 | const strLength = str.length; 120 | let result = ''; 121 | let i = 0; 122 | while (i < strLength) { 123 | const c = str.charCodeAt(i); 124 | if (c <= 255) { 125 | result += str[i++]; 126 | continue; 127 | } 128 | if (c >= highSurrogateFrom && c <= highSurrogateTo) { 129 | result += '&#' + getCodePoint(str, i) + ';'; 130 | i+=2; 131 | } else { 132 | result += '&#' + c + ';'; 133 | i++; 134 | } 135 | } 136 | return result; 137 | } 138 | 139 | static encodeNonASCII(str: string) { 140 | return new Html5Entities().encodeNonASCII(str); 141 | } 142 | } 143 | 144 | function createIndexes(alphaIndex: {[k: string]: string}, charIndex: {[k: string]: {[k: string]: string}}) { 145 | let i = ENTITIES.length; 146 | while (i--) { 147 | const [alpha, [chr, chr2]] = ENTITIES[i]; 148 | const addChar = (chr < 32 || chr > 126) || chr === 62 || chr === 60 || chr === 38 || chr === 34 || chr === 39; 149 | let charInfo: {[key: string]: string}; 150 | if (addChar) { 151 | charInfo = charIndex[chr] = charIndex[chr] || {}; 152 | } 153 | if (chr2) { 154 | alphaIndex[alpha] = String.fromCharCode(chr) + String.fromCharCode(chr2); 155 | addChar && (charInfo![chr2] = alpha); 156 | } else { 157 | alphaIndex[alpha] = String.fromCharCode(chr); 158 | addChar && (charInfo![''] = alpha); 159 | } 160 | } 161 | 162 | i = DECODE_ONLY_ENTITIES.length; 163 | while (i--) { 164 | const [alpha, [chr, chr2]] = DECODE_ONLY_ENTITIES[i]; 165 | alphaIndex[alpha] = String.fromCharCode(chr) + (chr2 ? String.fromCharCode(chr2) : ''); 166 | } 167 | } 168 | -------------------------------------------------------------------------------- /benchmark/old/index.ts: -------------------------------------------------------------------------------- 1 | export {XmlEntities} from './xml-entities'; 2 | export {Html4Entities} from './html4-entities'; 3 | export {Html5Entities} from './html5-entities'; 4 | -------------------------------------------------------------------------------- /benchmark/old/xml-entities.ts: -------------------------------------------------------------------------------- 1 | import {fromCodePoint, getCodePoint, highSurrogateFrom, highSurrogateTo} from '../../src/surrogate-pairs'; 2 | 3 | const ALPHA_INDEX: {[entity: string]: string} = { 4 | '<': '<', 5 | '>': '>', 6 | '"': '"', 7 | '&apos': '\'', 8 | '&': '&', 9 | '<': '<', 10 | '>': '>', 11 | '"': '"', 12 | ''': '\'', 13 | '&': '&' 14 | }; 15 | 16 | const CHAR_INDEX: {[charCode: string]: string} = { 17 | 60: 'lt', 18 | 62: 'gt', 19 | 34: 'quot', 20 | 39: 'apos', 21 | 38: 'amp' 22 | }; 23 | 24 | const CHAR_S_INDEX: {[char: string]: string} = { 25 | '<': '<', 26 | '>': '>', 27 | '"': '"', 28 | '\'': ''', 29 | '&': '&' 30 | }; 31 | 32 | export class XmlEntities { 33 | encode(str: string) { 34 | if (!str || !str.length) { 35 | return ''; 36 | } 37 | return str.replace(/[<>"'&]/g, function (s) { 38 | return CHAR_S_INDEX[s]; 39 | }); 40 | } 41 | 42 | static encode(str: string) { 43 | return new XmlEntities().encode(str); 44 | } 45 | 46 | decode(str: string) { 47 | if (!str || !str.length) { 48 | return ''; 49 | } 50 | return str.replace(/&#?[0-9a-zA-Z]+;?/g, function (s) { 51 | if (s.charAt(1) === '#') { 52 | const code = s.charAt(2).toLowerCase() === 'x' ? 53 | parseInt(s.substr(3), 16) : 54 | parseInt(s.substr(2)); 55 | 56 | if (!isNaN(code) || code >= -32768) { 57 | if (code <= 65535) { 58 | return String.fromCharCode(code); 59 | } else { 60 | return fromCodePoint(code); 61 | } 62 | } 63 | return ''; 64 | } 65 | return ALPHA_INDEX[s] || s; 66 | }); 67 | } 68 | 69 | static decode(str: string) { 70 | return new XmlEntities().decode(str); 71 | } 72 | 73 | encodeNonUTF(str: string) { 74 | if (!str || !str.length) { 75 | return ''; 76 | } 77 | const strLength = str.length; 78 | let result = ''; 79 | let i = 0; 80 | while (i < strLength) { 81 | const c = str.charCodeAt(i); 82 | const alpha = CHAR_INDEX[c]; 83 | if (alpha) { 84 | result += "&" + alpha + ";"; 85 | i++; 86 | continue; 87 | } 88 | if (c < 32 || c > 126) { 89 | if (c >= highSurrogateFrom && c <= highSurrogateTo) { 90 | result += '&#' + getCodePoint(str, i) + ';'; 91 | i++; 92 | } else { 93 | result += '&#' + c + ';'; 94 | } 95 | } else { 96 | result += str.charAt(i); 97 | } 98 | i++; 99 | } 100 | return result; 101 | } 102 | 103 | static encodeNonUTF(str: string) { 104 | return new XmlEntities().encodeNonUTF(str); 105 | } 106 | 107 | encodeNonASCII(str: string) { 108 | if (!str || !str.length) { 109 | return ''; 110 | } 111 | const strLength = str.length; 112 | let result = ''; 113 | let i = 0; 114 | while (i < strLength) { 115 | const c = str.charCodeAt(i); 116 | if (c <= 255) { 117 | result += str[i++]; 118 | continue; 119 | } 120 | if (c >= highSurrogateFrom && c <= highSurrogateTo) { 121 | result += '&#' + getCodePoint(str, i) + ';'; 122 | i++; 123 | } else { 124 | result += '&#' + c + ';'; 125 | } 126 | i++; 127 | } 128 | return result; 129 | } 130 | 131 | static encodeNonASCII(str: string) { 132 | return new XmlEntities().encodeNonASCII(str); 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /benchmark/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} 2 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import eslint from '@eslint/js'; 2 | import eslintConfigPrettier from "eslint-config-prettier/flat"; 3 | import importPlugin from 'eslint-plugin-import'; 4 | import tseslint from 'typescript-eslint'; 5 | 6 | export default tseslint.config( 7 | eslint.configs.recommended, 8 | tseslint.configs.recommended, 9 | eslintConfigPrettier, 10 | importPlugin.flatConfigs.recommended, 11 | importPlugin.flatConfigs.typescript, 12 | { 13 | rules: { 14 | "@typescript-eslint/no-explicit-any": "off", 15 | "@typescript-eslint/no-empty-interface": "error", 16 | "@typescript-eslint/no-non-null-assertion": "off", 17 | "no-irregular-whitespace": "off", 18 | "no-control-regex": "off", 19 | "no-duplicate-imports": ["error", {"includeExports": true}], 20 | "arrow-body-style": ["error", "as-needed"], 21 | "no-restricted-globals": ["error", "name", "toString", "pending"], 22 | "import/no-unresolved": "off", 23 | "import/order": ["error", { 24 | "groups": ["builtin", "external", "internal", "sibling"], 25 | "pathGroupsExcludedImportTypes": ["parent", "sibling", "index"], 26 | "alphabetize": { 27 | "order": "asc", 28 | "caseInsensitive": true 29 | } 30 | }] 31 | }, 32 | ignores: ["dist"] 33 | } 34 | ); 35 | -------------------------------------------------------------------------------- /jest.config.ts: -------------------------------------------------------------------------------- 1 | import { createDefaultEsmPreset, createDefaultPreset } from 'ts-jest' 2 | 3 | export default { 4 | ...(process.env.TEST_DIST === 'esm' ? 5 | createDefaultEsmPreset({tsconfig: 'tsconfig.esm.json', diagnostics: { ignoreCodes: ['TS151001'] }}) : 6 | createDefaultPreset()), 7 | ...(!process.env.TEST_DIST ? { 8 | moduleNameMapper: { 9 | '^(.+)\\.js$': '$1', 10 | } 11 | } : {}), 12 | testPathIgnorePatterns: ["/node_modules/", "/dist/", "/test-apps/"], 13 | } 14 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "html-entities", 3 | "version": "2.6.0", 4 | "description": "Fastest HTML entities encode/decode library.", 5 | "keywords": [ 6 | "html", 7 | "html entities", 8 | "html entities encode", 9 | "html entities decode", 10 | "entities", 11 | "entities encode", 12 | "entities decode" 13 | ], 14 | "author": { 15 | "name": "Marat Dulin", 16 | "email": "mdevils@yandex.ru" 17 | }, 18 | "funding": [ 19 | { 20 | "type": "github", 21 | "url": "https://github.com/sponsors/mdevils" 22 | }, 23 | { 24 | "type": "patreon", 25 | "url": "https://patreon.com/mdevils" 26 | } 27 | ], 28 | "devDependencies": { 29 | "@commitlint/cli": "^17.6.6", 30 | "@commitlint/config-conventional": "^17.6.6", 31 | "@types/benchmark": "^2.1.0", 32 | "@types/he": "^1.1.1", 33 | "@types/jest": "^29.5.14", 34 | "@types/node": "^22.13.14", 35 | "benchmark": "^2.1.4", 36 | "entities": "^6.0.0", 37 | "eslint": "^9.23.0", 38 | "eslint-config-prettier": "^10.1.1", 39 | "eslint-plugin-import": "^2.31.0", 40 | "flowgen": "^1.13.0", 41 | "he": "^1.2.0", 42 | "husky": "^4.3.6", 43 | "prettier": "^3.5.3", 44 | "standard-version": "^9.5.0", 45 | "ts-jest": "^29.3.0", 46 | "tshy": "^3.0.2", 47 | "tsx": "^4.19.3", 48 | "typescript": "^5.8.2", 49 | "typescript-eslint": "^8.28.0" 50 | }, 51 | "repository": { 52 | "type": "git", 53 | "url": "https://github.com/mdevils/html-entities.git" 54 | }, 55 | "sideEffects": false, 56 | "main": "./dist/commonjs/index.js", 57 | "typings": "./dist/commonjs/index.d.ts", 58 | "types": "./dist/commonjs/index.d.ts", 59 | "scripts": { 60 | "test": "jest", 61 | "test:watch": "jest --watch", 62 | "test:dist": "npm run test:dist:commonjs && npm run test:dist:esm", 63 | "test:dist:commonjs": "TEST_DIST=commonjs npm run test", 64 | "test:dist:esm": "TEST_DIST=esm node --experimental-vm-modules node_modules/.bin/jest", 65 | "benchmark": "tsx benchmark/benchmark", 66 | "lint": "eslint src/**.ts", 67 | "flow-type-gen": "flowgen --add-flow-header dist/commonjs/index.d.ts -o dist/commonjs/index.js.flow", 68 | "remove-unused-declarations": "find dist -type f \\( -name '*.d.ts' ! -name index.d.ts \\) | xargs rm", 69 | "build": "rm -Rf dist && tsc --noEmit && tshy && tsc --declaration --emitDeclarationOnly -p tsconfig.json && tsc --emitDeclarationOnly -p tsconfig.esm.json && npm run remove-unused-declarations && npm run flow-type-gen", 70 | "prepublishOnly": "npm run build && npm run test && npm run test:dist", 71 | "release": "standard-version" 72 | }, 73 | "files": [ 74 | "dist", 75 | "src", 76 | "LICENSE" 77 | ], 78 | "husky": { 79 | "hooks": { 80 | "pre-commit": "npm run lint && npm run test", 81 | "commit-msg": "commitlint -E HUSKY_GIT_PARAMS" 82 | } 83 | }, 84 | "license": "MIT", 85 | "type": "module", 86 | "tshy": { 87 | "exports": { 88 | "./package.json": "./package.json", 89 | ".": "./src/index.ts" 90 | }, 91 | "selfLink": false 92 | }, 93 | "exports": { 94 | "./package.json": "./package.json", 95 | ".": { 96 | "import": { 97 | "types": "./dist/esm/index.d.ts", 98 | "default": "./dist/esm/index.js" 99 | }, 100 | "require": { 101 | "types": "./dist/commonjs/index.d.ts", 102 | "default": "./dist/commonjs/index.js" 103 | } 104 | } 105 | }, 106 | "module": "./dist/esm/index.js", 107 | "commitlint": { 108 | "extends": [ 109 | "@commitlint/config-conventional" 110 | ], 111 | "rules": { 112 | "subject-case": [ 113 | 2, 114 | "never", 115 | [ 116 | "start-case", 117 | "pascal-case" 118 | ] 119 | ] 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | import {bodyRegExps, namedReferences} from './named-references.js'; 2 | import {numericUnicodeMap} from './numeric-unicode-map.js'; 3 | import {fromCodePoint, getCodePoint} from './surrogate-pairs.js'; 4 | 5 | const allNamedReferences = { 6 | ...namedReferences, 7 | all: namedReferences.html5 8 | }; 9 | 10 | export type Level = 'xml' | 'html4' | 'html5' | 'all'; 11 | 12 | interface CommonOptions { 13 | level?: Level; 14 | } 15 | 16 | export type EncodeMode = 'specialChars' | 'nonAscii' | 'nonAsciiPrintable' | 'nonAsciiPrintableOnly' | 'extensive'; 17 | 18 | export interface EncodeOptions extends CommonOptions { 19 | mode?: EncodeMode; 20 | numeric?: 'decimal' | 'hexadecimal'; 21 | } 22 | 23 | export type DecodeScope = 'strict' | 'body' | 'attribute'; 24 | 25 | export interface DecodeOptions extends CommonOptions { 26 | scope?: DecodeScope; 27 | } 28 | 29 | const encodeRegExps: Record = { 30 | specialChars: /[<>'"&]/g, 31 | nonAscii: /[<>'"&\u0080-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, 32 | nonAsciiPrintable: /[<>'"&\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, 33 | nonAsciiPrintableOnly: /[\x01-\x08\x11-\x15\x17-\x1F\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g, 34 | extensive: /[\x01-\x0c\x0e-\x1f\x21-\x2c\x2e-\x2f\x3a-\x40\x5b-\x60\x7b-\x7d\x7f-\uD7FF\uE000-\uFFFF\uDC00-\uDFFF]|[\uD800-\uDBFF][\uDC00-\uDFFF]?/g 35 | }; 36 | 37 | const defaultEncodeOptions: EncodeOptions = { 38 | mode: 'specialChars', 39 | level: 'all', 40 | numeric: 'decimal' 41 | }; 42 | 43 | /** Encodes all the necessary (specified by `level`) characters in the text */ 44 | export function encode( 45 | text: string | undefined | null, 46 | {mode = 'specialChars', numeric = 'decimal', level = 'all'}: EncodeOptions = defaultEncodeOptions 47 | ) { 48 | if (!text) { 49 | return ''; 50 | } 51 | 52 | const encodeRegExp = encodeRegExps[mode]; 53 | const references = allNamedReferences[level].characters; 54 | const isHex = numeric === 'hexadecimal'; 55 | 56 | return String.prototype.replace.call(text, encodeRegExp, (input) => { 57 | let result = references[input]; 58 | if (!result) { 59 | const code = input.length > 1 ? getCodePoint(input, 0)! : input.charCodeAt(0); 60 | result = (isHex ? '&#x' + code.toString(16) : '&#' + code) + ';'; 61 | } 62 | return result; 63 | }); 64 | } 65 | 66 | const defaultDecodeOptions: DecodeOptions = { 67 | scope: 'body', 68 | level: 'all' 69 | }; 70 | 71 | const strict = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);/g; 72 | const attribute = /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+)[;=]?/g; 73 | 74 | const baseDecodeRegExps: Record, Record> = { 75 | xml: { 76 | strict, 77 | attribute, 78 | body: bodyRegExps.xml 79 | }, 80 | html4: { 81 | strict, 82 | attribute, 83 | body: bodyRegExps.html4 84 | }, 85 | html5: { 86 | strict, 87 | attribute, 88 | body: bodyRegExps.html5 89 | } 90 | }; 91 | 92 | const decodeRegExps: Record> = { 93 | ...baseDecodeRegExps, 94 | all: baseDecodeRegExps.html5 95 | }; 96 | 97 | const fromCharCode = String.fromCharCode; 98 | const outOfBoundsChar = fromCharCode(65533); 99 | 100 | const defaultDecodeEntityOptions: CommonOptions = { 101 | level: 'all' 102 | }; 103 | 104 | function getDecodedEntity( 105 | entity: string, 106 | references: Record, 107 | isAttribute: boolean, 108 | isStrict: boolean 109 | ): string { 110 | let decodeResult = entity; 111 | const decodeEntityLastChar = entity[entity.length - 1]; 112 | if (isAttribute && decodeEntityLastChar === '=') { 113 | decodeResult = entity; 114 | } else if (isStrict && decodeEntityLastChar !== ';') { 115 | decodeResult = entity; 116 | } else { 117 | const decodeResultByReference = references[entity]; 118 | if (decodeResultByReference) { 119 | decodeResult = decodeResultByReference; 120 | } else if (entity[0] === '&' && entity[1] === '#') { 121 | const decodeSecondChar = entity[2]; 122 | const decodeCode = 123 | decodeSecondChar == 'x' || decodeSecondChar == 'X' 124 | ? parseInt(entity.substr(3), 16) 125 | : parseInt(entity.substr(2)); 126 | 127 | decodeResult = 128 | decodeCode >= 0x10ffff 129 | ? outOfBoundsChar 130 | : decodeCode > 65535 131 | ? fromCodePoint(decodeCode) 132 | : fromCharCode(numericUnicodeMap[decodeCode] || decodeCode); 133 | } 134 | } 135 | return decodeResult; 136 | } 137 | 138 | /** Decodes a single entity */ 139 | export function decodeEntity( 140 | entity: string | undefined | null, 141 | {level = 'all'}: CommonOptions = defaultDecodeEntityOptions 142 | ): string { 143 | if (!entity) { 144 | return ''; 145 | } 146 | return getDecodedEntity(entity, allNamedReferences[level].entities, false, false); 147 | } 148 | 149 | /** Decodes all entities in the text */ 150 | export function decode( 151 | text: string | undefined | null, 152 | {level = 'all', scope = level === 'xml' ? 'strict' : 'body'}: DecodeOptions = defaultDecodeOptions 153 | ) { 154 | if (!text) { 155 | return ''; 156 | } 157 | 158 | const decodeRegExp = decodeRegExps[level][scope]; 159 | const references = allNamedReferences[level].entities; 160 | const isAttribute = scope === 'attribute'; 161 | const isStrict = scope === 'strict'; 162 | 163 | return text.replace(decodeRegExp, (entity) => getDecodedEntity(entity, references, isAttribute, isStrict)); 164 | } 165 | -------------------------------------------------------------------------------- /src/named-references.ts: -------------------------------------------------------------------------------- 1 | // This file is autogenerated by tools/process-named-references.ts 2 | const pairDivider = "~"; 3 | const blockDivider = "~~"; 4 | 5 | type GeneratedReferences = { 6 | entities: Record; 7 | characters: Record; 8 | }; 9 | 10 | function generateNamedReferences(input: string, prev?: GeneratedReferences): GeneratedReferences { 11 | const entities: Record = {}; 12 | const characters: Record = {}; 13 | const blocks = input.split(blockDivider); 14 | let isOptionalBlock = false; 15 | for (let i = 0; blocks.length > i; i++) { 16 | const entries = blocks[i].split(pairDivider); 17 | for (let j = 0; j < entries.length; j+=2) { 18 | const entity = entries[j]; 19 | const character = entries[j+1]; 20 | const fullEntity = '&' + entity + ';'; 21 | entities[fullEntity] = character; 22 | if (isOptionalBlock) { 23 | entities['&' + entity] = character; 24 | } 25 | characters[character] = fullEntity; 26 | } 27 | isOptionalBlock = true; 28 | } 29 | return prev ? 30 | {entities: {...entities, ...prev.entities}, characters: {...characters, ...prev.characters}} : 31 | {entities, characters}; 32 | } 33 | 34 | export type NamedReferences = { 35 | [K in 'xml' | 'html4' | 'html5']: { 36 | entities: Record; 37 | characters: Record; 38 | } 39 | }; 40 | export const bodyRegExps = { 41 | xml: /&(?:#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g, 42 | html4: /∉|&(?:nbsp|iexcl|cent|pound|curren|yen|brvbar|sect|uml|copy|ordf|laquo|not|shy|reg|macr|deg|plusmn|sup2|sup3|acute|micro|para|middot|cedil|sup1|ordm|raquo|frac14|frac12|frac34|iquest|Agrave|Aacute|Acirc|Atilde|Auml|Aring|AElig|Ccedil|Egrave|Eacute|Ecirc|Euml|Igrave|Iacute|Icirc|Iuml|ETH|Ntilde|Ograve|Oacute|Ocirc|Otilde|Ouml|times|Oslash|Ugrave|Uacute|Ucirc|Uuml|Yacute|THORN|szlig|agrave|aacute|acirc|atilde|auml|aring|aelig|ccedil|egrave|eacute|ecirc|euml|igrave|iacute|icirc|iuml|eth|ntilde|ograve|oacute|ocirc|otilde|ouml|divide|oslash|ugrave|uacute|ucirc|uuml|yacute|thorn|yuml|quot|amp|lt|gt|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g, 43 | html5: /·|℗|⋇|⪧|⩺|⋗|⦕|⩼|⪆|⥸|⋗|⋛|⪌|≷|≳|⪦|⩹|⋖|⋋|⋉|⥶|⩻|⦖|◃|⊴|◂|∉|⋹̸|⋵̸|∉|⋷|⋶|∌|∌|⋾|⋽|∥|⊠|⨱|⨰|&(?:AElig|AMP|Aacute|Acirc|Agrave|Aring|Atilde|Auml|COPY|Ccedil|ETH|Eacute|Ecirc|Egrave|Euml|GT|Iacute|Icirc|Igrave|Iuml|LT|Ntilde|Oacute|Ocirc|Ograve|Oslash|Otilde|Ouml|QUOT|REG|THORN|Uacute|Ucirc|Ugrave|Uuml|Yacute|aacute|acirc|acute|aelig|agrave|amp|aring|atilde|auml|brvbar|ccedil|cedil|cent|copy|curren|deg|divide|eacute|ecirc|egrave|eth|euml|frac12|frac14|frac34|gt|iacute|icirc|iexcl|igrave|iquest|iuml|laquo|lt|macr|micro|middot|nbsp|not|ntilde|oacute|ocirc|ograve|ordf|ordm|oslash|otilde|ouml|para|plusmn|pound|quot|raquo|reg|sect|shy|sup1|sup2|sup3|szlig|thorn|times|uacute|ucirc|ugrave|uml|uuml|yacute|yen|yuml|#\d+|#[xX][\da-fA-F]+|[0-9a-zA-Z]+);?/g 44 | }; 45 | export const namedReferences = {} as NamedReferences; 46 | namedReferences['xml'] = generateNamedReferences("lt~<~gt~>~quot~\"~apos~'~amp~&"); 47 | namedReferences['html4'] = generateNamedReferences("apos~'~OElig~Œ~oelig~œ~Scaron~Š~scaron~š~Yuml~Ÿ~circ~ˆ~tilde~˜~ensp~ ~emsp~ ~thinsp~ ~zwnj~‌~zwj~‍~lrm~‎~rlm~‏~ndash~–~mdash~—~lsquo~‘~rsquo~’~sbquo~‚~ldquo~“~rdquo~”~bdquo~„~dagger~†~Dagger~‡~permil~‰~lsaquo~‹~rsaquo~›~euro~€~fnof~ƒ~Alpha~Α~Beta~Β~Gamma~Γ~Delta~Δ~Epsilon~Ε~Zeta~Ζ~Eta~Η~Theta~Θ~Iota~Ι~Kappa~Κ~Lambda~Λ~Mu~Μ~Nu~Ν~Xi~Ξ~Omicron~Ο~Pi~Π~Rho~Ρ~Sigma~Σ~Tau~Τ~Upsilon~Υ~Phi~Φ~Chi~Χ~Psi~Ψ~Omega~Ω~alpha~α~beta~β~gamma~γ~delta~δ~epsilon~ε~zeta~ζ~eta~η~theta~θ~iota~ι~kappa~κ~lambda~λ~mu~μ~nu~ν~xi~ξ~omicron~ο~pi~π~rho~ρ~sigmaf~ς~sigma~σ~tau~τ~upsilon~υ~phi~φ~chi~χ~psi~ψ~omega~ω~thetasym~ϑ~upsih~ϒ~piv~ϖ~bull~•~hellip~…~prime~′~Prime~″~oline~‾~frasl~⁄~weierp~℘~image~ℑ~real~ℜ~trade~™~alefsym~ℵ~larr~←~uarr~↑~rarr~→~darr~↓~harr~↔~crarr~↵~lArr~⇐~uArr~⇑~rArr~⇒~dArr~⇓~hArr~⇔~forall~∀~part~∂~exist~∃~empty~∅~nabla~∇~isin~∈~notin~∉~ni~∋~prod~∏~sum~∑~minus~−~lowast~∗~radic~√~prop~∝~infin~∞~ang~∠~and~∧~or~∨~cap~∩~cup~∪~int~∫~there4~∴~sim~∼~cong~≅~asymp~≈~ne~≠~equiv~≡~le~≤~ge~≥~sub~⊂~sup~⊃~nsub~⊄~sube~⊆~supe~⊇~oplus~⊕~otimes~⊗~perp~⊥~sdot~⋅~lceil~⌈~rceil~⌉~lfloor~⌊~rfloor~⌋~lang~〈~rang~〉~loz~◊~spades~♠~clubs~♣~hearts~♥~diams~♦~~nbsp~ ~iexcl~¡~cent~¢~pound~£~curren~¤~yen~¥~brvbar~¦~sect~§~uml~¨~copy~©~ordf~ª~laquo~«~not~¬~shy~­~reg~®~macr~¯~deg~°~plusmn~±~sup2~²~sup3~³~acute~´~micro~µ~para~¶~middot~·~cedil~¸~sup1~¹~ordm~º~raquo~»~frac14~¼~frac12~½~frac34~¾~iquest~¿~Agrave~À~Aacute~Á~Acirc~Â~Atilde~Ã~Auml~Ä~Aring~Å~AElig~Æ~Ccedil~Ç~Egrave~È~Eacute~É~Ecirc~Ê~Euml~Ë~Igrave~Ì~Iacute~Í~Icirc~Î~Iuml~Ï~ETH~Ð~Ntilde~Ñ~Ograve~Ò~Oacute~Ó~Ocirc~Ô~Otilde~Õ~Ouml~Ö~times~×~Oslash~Ø~Ugrave~Ù~Uacute~Ú~Ucirc~Û~Uuml~Ü~Yacute~Ý~THORN~Þ~szlig~ß~agrave~à~aacute~á~acirc~â~atilde~ã~auml~ä~aring~å~aelig~æ~ccedil~ç~egrave~è~eacute~é~ecirc~ê~euml~ë~igrave~ì~iacute~í~icirc~î~iuml~ï~eth~ð~ntilde~ñ~ograve~ò~oacute~ó~ocirc~ô~otilde~õ~ouml~ö~divide~÷~oslash~ø~ugrave~ù~uacute~ú~ucirc~û~uuml~ü~yacute~ý~thorn~þ~yuml~ÿ~quot~\"~amp~&~lt~<~gt~>"); 48 | namedReferences['html5'] = generateNamedReferences("Abreve~Ă~Acy~А~Afr~𝔄~Amacr~Ā~And~⩓~Aogon~Ą~Aopf~𝔸~ApplyFunction~⁡~Ascr~𝒜~Assign~≔~Backslash~∖~Barv~⫧~Barwed~⌆~Bcy~Б~Because~∵~Bernoullis~ℬ~Bfr~𝔅~Bopf~𝔹~Breve~˘~Bscr~ℬ~Bumpeq~≎~CHcy~Ч~Cacute~Ć~Cap~⋒~CapitalDifferentialD~ⅅ~Cayleys~ℭ~Ccaron~Č~Ccirc~Ĉ~Cconint~∰~Cdot~Ċ~Cedilla~¸~CenterDot~·~Cfr~ℭ~CircleDot~⊙~CircleMinus~⊖~CirclePlus~⊕~CircleTimes~⊗~ClockwiseContourIntegral~∲~CloseCurlyDoubleQuote~”~CloseCurlyQuote~’~Colon~∷~Colone~⩴~Congruent~≡~Conint~∯~ContourIntegral~∮~Copf~ℂ~Coproduct~∐~CounterClockwiseContourIntegral~∳~Cross~⨯~Cscr~𝒞~Cup~⋓~CupCap~≍~DD~ⅅ~DDotrahd~⤑~DJcy~Ђ~DScy~Ѕ~DZcy~Џ~Darr~↡~Dashv~⫤~Dcaron~Ď~Dcy~Д~Del~∇~Dfr~𝔇~DiacriticalAcute~´~DiacriticalDot~˙~DiacriticalDoubleAcute~˝~DiacriticalGrave~`~DiacriticalTilde~˜~Diamond~⋄~DifferentialD~ⅆ~Dopf~𝔻~Dot~¨~DotDot~⃜~DotEqual~≐~DoubleContourIntegral~∯~DoubleDot~¨~DoubleDownArrow~⇓~DoubleLeftArrow~⇐~DoubleLeftRightArrow~⇔~DoubleLeftTee~⫤~DoubleLongLeftArrow~⟸~DoubleLongLeftRightArrow~⟺~DoubleLongRightArrow~⟹~DoubleRightArrow~⇒~DoubleRightTee~⊨~DoubleUpArrow~⇑~DoubleUpDownArrow~⇕~DoubleVerticalBar~∥~DownArrow~↓~DownArrowBar~⤓~DownArrowUpArrow~⇵~DownBreve~̑~DownLeftRightVector~⥐~DownLeftTeeVector~⥞~DownLeftVector~↽~DownLeftVectorBar~⥖~DownRightTeeVector~⥟~DownRightVector~⇁~DownRightVectorBar~⥗~DownTee~⊤~DownTeeArrow~↧~Downarrow~⇓~Dscr~𝒟~Dstrok~Đ~ENG~Ŋ~Ecaron~Ě~Ecy~Э~Edot~Ė~Efr~𝔈~Element~∈~Emacr~Ē~EmptySmallSquare~◻~EmptyVerySmallSquare~▫~Eogon~Ę~Eopf~𝔼~Equal~⩵~EqualTilde~≂~Equilibrium~⇌~Escr~ℰ~Esim~⩳~Exists~∃~ExponentialE~ⅇ~Fcy~Ф~Ffr~𝔉~FilledSmallSquare~◼~FilledVerySmallSquare~▪~Fopf~𝔽~ForAll~∀~Fouriertrf~ℱ~Fscr~ℱ~GJcy~Ѓ~Gammad~Ϝ~Gbreve~Ğ~Gcedil~Ģ~Gcirc~Ĝ~Gcy~Г~Gdot~Ġ~Gfr~𝔊~Gg~⋙~Gopf~𝔾~GreaterEqual~≥~GreaterEqualLess~⋛~GreaterFullEqual~≧~GreaterGreater~⪢~GreaterLess~≷~GreaterSlantEqual~⩾~GreaterTilde~≳~Gscr~𝒢~Gt~≫~HARDcy~Ъ~Hacek~ˇ~Hat~^~Hcirc~Ĥ~Hfr~ℌ~HilbertSpace~ℋ~Hopf~ℍ~HorizontalLine~─~Hscr~ℋ~Hstrok~Ħ~HumpDownHump~≎~HumpEqual~≏~IEcy~Е~IJlig~IJ~IOcy~Ё~Icy~И~Idot~İ~Ifr~ℑ~Im~ℑ~Imacr~Ī~ImaginaryI~ⅈ~Implies~⇒~Int~∬~Integral~∫~Intersection~⋂~InvisibleComma~⁣~InvisibleTimes~⁢~Iogon~Į~Iopf~𝕀~Iscr~ℐ~Itilde~Ĩ~Iukcy~І~Jcirc~Ĵ~Jcy~Й~Jfr~𝔍~Jopf~𝕁~Jscr~𝒥~Jsercy~Ј~Jukcy~Є~KHcy~Х~KJcy~Ќ~Kcedil~Ķ~Kcy~К~Kfr~𝔎~Kopf~𝕂~Kscr~𝒦~LJcy~Љ~Lacute~Ĺ~Lang~⟪~Laplacetrf~ℒ~Larr~↞~Lcaron~Ľ~Lcedil~Ļ~Lcy~Л~LeftAngleBracket~⟨~LeftArrow~←~LeftArrowBar~⇤~LeftArrowRightArrow~⇆~LeftCeiling~⌈~LeftDoubleBracket~⟦~LeftDownTeeVector~⥡~LeftDownVector~⇃~LeftDownVectorBar~⥙~LeftFloor~⌊~LeftRightArrow~↔~LeftRightVector~⥎~LeftTee~⊣~LeftTeeArrow~↤~LeftTeeVector~⥚~LeftTriangle~⊲~LeftTriangleBar~⧏~LeftTriangleEqual~⊴~LeftUpDownVector~⥑~LeftUpTeeVector~⥠~LeftUpVector~↿~LeftUpVectorBar~⥘~LeftVector~↼~LeftVectorBar~⥒~Leftarrow~⇐~Leftrightarrow~⇔~LessEqualGreater~⋚~LessFullEqual~≦~LessGreater~≶~LessLess~⪡~LessSlantEqual~⩽~LessTilde~≲~Lfr~𝔏~Ll~⋘~Lleftarrow~⇚~Lmidot~Ŀ~LongLeftArrow~⟵~LongLeftRightArrow~⟷~LongRightArrow~⟶~Longleftarrow~⟸~Longleftrightarrow~⟺~Longrightarrow~⟹~Lopf~𝕃~LowerLeftArrow~↙~LowerRightArrow~↘~Lscr~ℒ~Lsh~↰~Lstrok~Ł~Lt~≪~Map~⤅~Mcy~М~MediumSpace~ ~Mellintrf~ℳ~Mfr~𝔐~MinusPlus~∓~Mopf~𝕄~Mscr~ℳ~NJcy~Њ~Nacute~Ń~Ncaron~Ň~Ncedil~Ņ~Ncy~Н~NegativeMediumSpace~​~NegativeThickSpace~​~NegativeThinSpace~​~NegativeVeryThinSpace~​~NestedGreaterGreater~≫~NestedLessLess~≪~NewLine~\n~Nfr~𝔑~NoBreak~⁠~NonBreakingSpace~ ~Nopf~ℕ~Not~⫬~NotCongruent~≢~NotCupCap~≭~NotDoubleVerticalBar~∦~NotElement~∉~NotEqual~≠~NotEqualTilde~≂̸~NotExists~∄~NotGreater~≯~NotGreaterEqual~≱~NotGreaterFullEqual~≧̸~NotGreaterGreater~≫̸~NotGreaterLess~≹~NotGreaterSlantEqual~⩾̸~NotGreaterTilde~≵~NotHumpDownHump~≎̸~NotHumpEqual~≏̸~NotLeftTriangle~⋪~NotLeftTriangleBar~⧏̸~NotLeftTriangleEqual~⋬~NotLess~≮~NotLessEqual~≰~NotLessGreater~≸~NotLessLess~≪̸~NotLessSlantEqual~⩽̸~NotLessTilde~≴~NotNestedGreaterGreater~⪢̸~NotNestedLessLess~⪡̸~NotPrecedes~⊀~NotPrecedesEqual~⪯̸~NotPrecedesSlantEqual~⋠~NotReverseElement~∌~NotRightTriangle~⋫~NotRightTriangleBar~⧐̸~NotRightTriangleEqual~⋭~NotSquareSubset~⊏̸~NotSquareSubsetEqual~⋢~NotSquareSuperset~⊐̸~NotSquareSupersetEqual~⋣~NotSubset~⊂⃒~NotSubsetEqual~⊈~NotSucceeds~⊁~NotSucceedsEqual~⪰̸~NotSucceedsSlantEqual~⋡~NotSucceedsTilde~≿̸~NotSuperset~⊃⃒~NotSupersetEqual~⊉~NotTilde~≁~NotTildeEqual~≄~NotTildeFullEqual~≇~NotTildeTilde~≉~NotVerticalBar~∤~Nscr~𝒩~Ocy~О~Odblac~Ő~Ofr~𝔒~Omacr~Ō~Oopf~𝕆~OpenCurlyDoubleQuote~“~OpenCurlyQuote~‘~Or~⩔~Oscr~𝒪~Otimes~⨷~OverBar~‾~OverBrace~⏞~OverBracket~⎴~OverParenthesis~⏜~PartialD~∂~Pcy~П~Pfr~𝔓~PlusMinus~±~Poincareplane~ℌ~Popf~ℙ~Pr~⪻~Precedes~≺~PrecedesEqual~⪯~PrecedesSlantEqual~≼~PrecedesTilde~≾~Product~∏~Proportion~∷~Proportional~∝~Pscr~𝒫~Qfr~𝔔~Qopf~ℚ~Qscr~𝒬~RBarr~⤐~Racute~Ŕ~Rang~⟫~Rarr~↠~Rarrtl~⤖~Rcaron~Ř~Rcedil~Ŗ~Rcy~Р~Re~ℜ~ReverseElement~∋~ReverseEquilibrium~⇋~ReverseUpEquilibrium~⥯~Rfr~ℜ~RightAngleBracket~⟩~RightArrow~→~RightArrowBar~⇥~RightArrowLeftArrow~⇄~RightCeiling~⌉~RightDoubleBracket~⟧~RightDownTeeVector~⥝~RightDownVector~⇂~RightDownVectorBar~⥕~RightFloor~⌋~RightTee~⊢~RightTeeArrow~↦~RightTeeVector~⥛~RightTriangle~⊳~RightTriangleBar~⧐~RightTriangleEqual~⊵~RightUpDownVector~⥏~RightUpTeeVector~⥜~RightUpVector~↾~RightUpVectorBar~⥔~RightVector~⇀~RightVectorBar~⥓~Rightarrow~⇒~Ropf~ℝ~RoundImplies~⥰~Rrightarrow~⇛~Rscr~ℛ~Rsh~↱~RuleDelayed~⧴~SHCHcy~Щ~SHcy~Ш~SOFTcy~Ь~Sacute~Ś~Sc~⪼~Scedil~Ş~Scirc~Ŝ~Scy~С~Sfr~𝔖~ShortDownArrow~↓~ShortLeftArrow~←~ShortRightArrow~→~ShortUpArrow~↑~SmallCircle~∘~Sopf~𝕊~Sqrt~√~Square~□~SquareIntersection~⊓~SquareSubset~⊏~SquareSubsetEqual~⊑~SquareSuperset~⊐~SquareSupersetEqual~⊒~SquareUnion~⊔~Sscr~𝒮~Star~⋆~Sub~⋐~Subset~⋐~SubsetEqual~⊆~Succeeds~≻~SucceedsEqual~⪰~SucceedsSlantEqual~≽~SucceedsTilde~≿~SuchThat~∋~Sum~∑~Sup~⋑~Superset~⊃~SupersetEqual~⊇~Supset~⋑~TRADE~™~TSHcy~Ћ~TScy~Ц~Tab~\t~Tcaron~Ť~Tcedil~Ţ~Tcy~Т~Tfr~𝔗~Therefore~∴~ThickSpace~  ~ThinSpace~ ~Tilde~∼~TildeEqual~≃~TildeFullEqual~≅~TildeTilde~≈~Topf~𝕋~TripleDot~⃛~Tscr~𝒯~Tstrok~Ŧ~Uarr~↟~Uarrocir~⥉~Ubrcy~Ў~Ubreve~Ŭ~Ucy~У~Udblac~Ű~Ufr~𝔘~Umacr~Ū~UnderBar~_~UnderBrace~⏟~UnderBracket~⎵~UnderParenthesis~⏝~Union~⋃~UnionPlus~⊎~Uogon~Ų~Uopf~𝕌~UpArrow~↑~UpArrowBar~⤒~UpArrowDownArrow~⇅~UpDownArrow~↕~UpEquilibrium~⥮~UpTee~⊥~UpTeeArrow~↥~Uparrow~⇑~Updownarrow~⇕~UpperLeftArrow~↖~UpperRightArrow~↗~Upsi~ϒ~Uring~Ů~Uscr~𝒰~Utilde~Ũ~VDash~⊫~Vbar~⫫~Vcy~В~Vdash~⊩~Vdashl~⫦~Vee~⋁~Verbar~‖~Vert~‖~VerticalBar~∣~VerticalLine~|~VerticalSeparator~❘~VerticalTilde~≀~VeryThinSpace~ ~Vfr~𝔙~Vopf~𝕍~Vscr~𝒱~Vvdash~⊪~Wcirc~Ŵ~Wedge~⋀~Wfr~𝔚~Wopf~𝕎~Wscr~𝒲~Xfr~𝔛~Xopf~𝕏~Xscr~𝒳~YAcy~Я~YIcy~Ї~YUcy~Ю~Ycirc~Ŷ~Ycy~Ы~Yfr~𝔜~Yopf~𝕐~Yscr~𝒴~ZHcy~Ж~Zacute~Ź~Zcaron~Ž~Zcy~З~Zdot~Ż~ZeroWidthSpace~​~Zfr~ℨ~Zopf~ℤ~Zscr~𝒵~abreve~ă~ac~∾~acE~∾̳~acd~∿~acy~а~af~⁡~afr~𝔞~aleph~ℵ~amacr~ā~amalg~⨿~andand~⩕~andd~⩜~andslope~⩘~andv~⩚~ange~⦤~angle~∠~angmsd~∡~angmsdaa~⦨~angmsdab~⦩~angmsdac~⦪~angmsdad~⦫~angmsdae~⦬~angmsdaf~⦭~angmsdag~⦮~angmsdah~⦯~angrt~∟~angrtvb~⊾~angrtvbd~⦝~angsph~∢~angst~Å~angzarr~⍼~aogon~ą~aopf~𝕒~ap~≈~apE~⩰~apacir~⩯~ape~≊~apid~≋~approx~≈~approxeq~≊~ascr~𝒶~ast~*~asympeq~≍~awconint~∳~awint~⨑~bNot~⫭~backcong~≌~backepsilon~϶~backprime~‵~backsim~∽~backsimeq~⋍~barvee~⊽~barwed~⌅~barwedge~⌅~bbrk~⎵~bbrktbrk~⎶~bcong~≌~bcy~б~becaus~∵~because~∵~bemptyv~⦰~bepsi~϶~bernou~ℬ~beth~ℶ~between~≬~bfr~𝔟~bigcap~⋂~bigcirc~◯~bigcup~⋃~bigodot~⨀~bigoplus~⨁~bigotimes~⨂~bigsqcup~⨆~bigstar~★~bigtriangledown~▽~bigtriangleup~△~biguplus~⨄~bigvee~⋁~bigwedge~⋀~bkarow~⤍~blacklozenge~⧫~blacksquare~▪~blacktriangle~▴~blacktriangledown~▾~blacktriangleleft~◂~blacktriangleright~▸~blank~␣~blk12~▒~blk14~░~blk34~▓~block~█~bne~=⃥~bnequiv~≡⃥~bnot~⌐~bopf~𝕓~bot~⊥~bottom~⊥~bowtie~⋈~boxDL~╗~boxDR~╔~boxDl~╖~boxDr~╓~boxH~═~boxHD~╦~boxHU~╩~boxHd~╤~boxHu~╧~boxUL~╝~boxUR~╚~boxUl~╜~boxUr~╙~boxV~║~boxVH~╬~boxVL~╣~boxVR~╠~boxVh~╫~boxVl~╢~boxVr~╟~boxbox~⧉~boxdL~╕~boxdR~╒~boxdl~┐~boxdr~┌~boxh~─~boxhD~╥~boxhU~╨~boxhd~┬~boxhu~┴~boxminus~⊟~boxplus~⊞~boxtimes~⊠~boxuL~╛~boxuR~╘~boxul~┘~boxur~└~boxv~│~boxvH~╪~boxvL~╡~boxvR~╞~boxvh~┼~boxvl~┤~boxvr~├~bprime~‵~breve~˘~bscr~𝒷~bsemi~⁏~bsim~∽~bsime~⋍~bsol~\\~bsolb~⧅~bsolhsub~⟈~bullet~•~bump~≎~bumpE~⪮~bumpe~≏~bumpeq~≏~cacute~ć~capand~⩄~capbrcup~⩉~capcap~⩋~capcup~⩇~capdot~⩀~caps~∩︀~caret~⁁~caron~ˇ~ccaps~⩍~ccaron~č~ccirc~ĉ~ccups~⩌~ccupssm~⩐~cdot~ċ~cemptyv~⦲~centerdot~·~cfr~𝔠~chcy~ч~check~✓~checkmark~✓~cir~○~cirE~⧃~circeq~≗~circlearrowleft~↺~circlearrowright~↻~circledR~®~circledS~Ⓢ~circledast~⊛~circledcirc~⊚~circleddash~⊝~cire~≗~cirfnint~⨐~cirmid~⫯~cirscir~⧂~clubsuit~♣~colon~:~colone~≔~coloneq~≔~comma~,~commat~@~comp~∁~compfn~∘~complement~∁~complexes~ℂ~congdot~⩭~conint~∮~copf~𝕔~coprod~∐~copysr~℗~cross~✗~cscr~𝒸~csub~⫏~csube~⫑~csup~⫐~csupe~⫒~ctdot~⋯~cudarrl~⤸~cudarrr~⤵~cuepr~⋞~cuesc~⋟~cularr~↶~cularrp~⤽~cupbrcap~⩈~cupcap~⩆~cupcup~⩊~cupdot~⊍~cupor~⩅~cups~∪︀~curarr~↷~curarrm~⤼~curlyeqprec~⋞~curlyeqsucc~⋟~curlyvee~⋎~curlywedge~⋏~curvearrowleft~↶~curvearrowright~↷~cuvee~⋎~cuwed~⋏~cwconint~∲~cwint~∱~cylcty~⌭~dHar~⥥~daleth~ℸ~dash~‐~dashv~⊣~dbkarow~⤏~dblac~˝~dcaron~ď~dcy~д~dd~ⅆ~ddagger~‡~ddarr~⇊~ddotseq~⩷~demptyv~⦱~dfisht~⥿~dfr~𝔡~dharl~⇃~dharr~⇂~diam~⋄~diamond~⋄~diamondsuit~♦~die~¨~digamma~ϝ~disin~⋲~div~÷~divideontimes~⋇~divonx~⋇~djcy~ђ~dlcorn~⌞~dlcrop~⌍~dollar~$~dopf~𝕕~dot~˙~doteq~≐~doteqdot~≑~dotminus~∸~dotplus~∔~dotsquare~⊡~doublebarwedge~⌆~downarrow~↓~downdownarrows~⇊~downharpoonleft~⇃~downharpoonright~⇂~drbkarow~⤐~drcorn~⌟~drcrop~⌌~dscr~𝒹~dscy~ѕ~dsol~⧶~dstrok~đ~dtdot~⋱~dtri~▿~dtrif~▾~duarr~⇵~duhar~⥯~dwangle~⦦~dzcy~џ~dzigrarr~⟿~eDDot~⩷~eDot~≑~easter~⩮~ecaron~ě~ecir~≖~ecolon~≕~ecy~э~edot~ė~ee~ⅇ~efDot~≒~efr~𝔢~eg~⪚~egs~⪖~egsdot~⪘~el~⪙~elinters~⏧~ell~ℓ~els~⪕~elsdot~⪗~emacr~ē~emptyset~∅~emptyv~∅~emsp13~ ~emsp14~ ~eng~ŋ~eogon~ę~eopf~𝕖~epar~⋕~eparsl~⧣~eplus~⩱~epsi~ε~epsiv~ϵ~eqcirc~≖~eqcolon~≕~eqsim~≂~eqslantgtr~⪖~eqslantless~⪕~equals~=~equest~≟~equivDD~⩸~eqvparsl~⧥~erDot~≓~erarr~⥱~escr~ℯ~esdot~≐~esim~≂~excl~!~expectation~ℰ~exponentiale~ⅇ~fallingdotseq~≒~fcy~ф~female~♀~ffilig~ffi~fflig~ff~ffllig~ffl~ffr~𝔣~filig~fi~fjlig~fj~flat~♭~fllig~fl~fltns~▱~fopf~𝕗~fork~⋔~forkv~⫙~fpartint~⨍~frac13~⅓~frac15~⅕~frac16~⅙~frac18~⅛~frac23~⅔~frac25~⅖~frac35~⅗~frac38~⅜~frac45~⅘~frac56~⅚~frac58~⅝~frac78~⅞~frown~⌢~fscr~𝒻~gE~≧~gEl~⪌~gacute~ǵ~gammad~ϝ~gap~⪆~gbreve~ğ~gcirc~ĝ~gcy~г~gdot~ġ~gel~⋛~geq~≥~geqq~≧~geqslant~⩾~ges~⩾~gescc~⪩~gesdot~⪀~gesdoto~⪂~gesdotol~⪄~gesl~⋛︀~gesles~⪔~gfr~𝔤~gg~≫~ggg~⋙~gimel~ℷ~gjcy~ѓ~gl~≷~glE~⪒~gla~⪥~glj~⪤~gnE~≩~gnap~⪊~gnapprox~⪊~gne~⪈~gneq~⪈~gneqq~≩~gnsim~⋧~gopf~𝕘~grave~`~gscr~ℊ~gsim~≳~gsime~⪎~gsiml~⪐~gtcc~⪧~gtcir~⩺~gtdot~⋗~gtlPar~⦕~gtquest~⩼~gtrapprox~⪆~gtrarr~⥸~gtrdot~⋗~gtreqless~⋛~gtreqqless~⪌~gtrless~≷~gtrsim~≳~gvertneqq~≩︀~gvnE~≩︀~hairsp~ ~half~½~hamilt~ℋ~hardcy~ъ~harrcir~⥈~harrw~↭~hbar~ℏ~hcirc~ĥ~heartsuit~♥~hercon~⊹~hfr~𝔥~hksearow~⤥~hkswarow~⤦~hoarr~⇿~homtht~∻~hookleftarrow~↩~hookrightarrow~↪~hopf~𝕙~horbar~―~hscr~𝒽~hslash~ℏ~hstrok~ħ~hybull~⁃~hyphen~‐~ic~⁣~icy~и~iecy~е~iff~⇔~ifr~𝔦~ii~ⅈ~iiiint~⨌~iiint~∭~iinfin~⧜~iiota~℩~ijlig~ij~imacr~ī~imagline~ℐ~imagpart~ℑ~imath~ı~imof~⊷~imped~Ƶ~in~∈~incare~℅~infintie~⧝~inodot~ı~intcal~⊺~integers~ℤ~intercal~⊺~intlarhk~⨗~intprod~⨼~iocy~ё~iogon~į~iopf~𝕚~iprod~⨼~iscr~𝒾~isinE~⋹~isindot~⋵~isins~⋴~isinsv~⋳~isinv~∈~it~⁢~itilde~ĩ~iukcy~і~jcirc~ĵ~jcy~й~jfr~𝔧~jmath~ȷ~jopf~𝕛~jscr~𝒿~jsercy~ј~jukcy~є~kappav~ϰ~kcedil~ķ~kcy~к~kfr~𝔨~kgreen~ĸ~khcy~х~kjcy~ќ~kopf~𝕜~kscr~𝓀~lAarr~⇚~lAtail~⤛~lBarr~⤎~lE~≦~lEg~⪋~lHar~⥢~lacute~ĺ~laemptyv~⦴~lagran~ℒ~langd~⦑~langle~⟨~lap~⪅~larrb~⇤~larrbfs~⤟~larrfs~⤝~larrhk~↩~larrlp~↫~larrpl~⤹~larrsim~⥳~larrtl~↢~lat~⪫~latail~⤙~late~⪭~lates~⪭︀~lbarr~⤌~lbbrk~❲~lbrace~{~lbrack~[~lbrke~⦋~lbrksld~⦏~lbrkslu~⦍~lcaron~ľ~lcedil~ļ~lcub~{~lcy~л~ldca~⤶~ldquor~„~ldrdhar~⥧~ldrushar~⥋~ldsh~↲~leftarrow~←~leftarrowtail~↢~leftharpoondown~↽~leftharpoonup~↼~leftleftarrows~⇇~leftrightarrow~↔~leftrightarrows~⇆~leftrightharpoons~⇋~leftrightsquigarrow~↭~leftthreetimes~⋋~leg~⋚~leq~≤~leqq~≦~leqslant~⩽~les~⩽~lescc~⪨~lesdot~⩿~lesdoto~⪁~lesdotor~⪃~lesg~⋚︀~lesges~⪓~lessapprox~⪅~lessdot~⋖~lesseqgtr~⋚~lesseqqgtr~⪋~lessgtr~≶~lesssim~≲~lfisht~⥼~lfr~𝔩~lg~≶~lgE~⪑~lhard~↽~lharu~↼~lharul~⥪~lhblk~▄~ljcy~љ~ll~≪~llarr~⇇~llcorner~⌞~llhard~⥫~lltri~◺~lmidot~ŀ~lmoust~⎰~lmoustache~⎰~lnE~≨~lnap~⪉~lnapprox~⪉~lne~⪇~lneq~⪇~lneqq~≨~lnsim~⋦~loang~⟬~loarr~⇽~lobrk~⟦~longleftarrow~⟵~longleftrightarrow~⟷~longmapsto~⟼~longrightarrow~⟶~looparrowleft~↫~looparrowright~↬~lopar~⦅~lopf~𝕝~loplus~⨭~lotimes~⨴~lowbar~_~lozenge~◊~lozf~⧫~lpar~(~lparlt~⦓~lrarr~⇆~lrcorner~⌟~lrhar~⇋~lrhard~⥭~lrtri~⊿~lscr~𝓁~lsh~↰~lsim~≲~lsime~⪍~lsimg~⪏~lsqb~[~lsquor~‚~lstrok~ł~ltcc~⪦~ltcir~⩹~ltdot~⋖~lthree~⋋~ltimes~⋉~ltlarr~⥶~ltquest~⩻~ltrPar~⦖~ltri~◃~ltrie~⊴~ltrif~◂~lurdshar~⥊~luruhar~⥦~lvertneqq~≨︀~lvnE~≨︀~mDDot~∺~male~♂~malt~✠~maltese~✠~map~↦~mapsto~↦~mapstodown~↧~mapstoleft~↤~mapstoup~↥~marker~▮~mcomma~⨩~mcy~м~measuredangle~∡~mfr~𝔪~mho~℧~mid~∣~midast~*~midcir~⫰~minusb~⊟~minusd~∸~minusdu~⨪~mlcp~⫛~mldr~…~mnplus~∓~models~⊧~mopf~𝕞~mp~∓~mscr~𝓂~mstpos~∾~multimap~⊸~mumap~⊸~nGg~⋙̸~nGt~≫⃒~nGtv~≫̸~nLeftarrow~⇍~nLeftrightarrow~⇎~nLl~⋘̸~nLt~≪⃒~nLtv~≪̸~nRightarrow~⇏~nVDash~⊯~nVdash~⊮~nacute~ń~nang~∠⃒~nap~≉~napE~⩰̸~napid~≋̸~napos~ʼn~napprox~≉~natur~♮~natural~♮~naturals~ℕ~nbump~≎̸~nbumpe~≏̸~ncap~⩃~ncaron~ň~ncedil~ņ~ncong~≇~ncongdot~⩭̸~ncup~⩂~ncy~н~neArr~⇗~nearhk~⤤~nearr~↗~nearrow~↗~nedot~≐̸~nequiv~≢~nesear~⤨~nesim~≂̸~nexist~∄~nexists~∄~nfr~𝔫~ngE~≧̸~nge~≱~ngeq~≱~ngeqq~≧̸~ngeqslant~⩾̸~nges~⩾̸~ngsim~≵~ngt~≯~ngtr~≯~nhArr~⇎~nharr~↮~nhpar~⫲~nis~⋼~nisd~⋺~niv~∋~njcy~њ~nlArr~⇍~nlE~≦̸~nlarr~↚~nldr~‥~nle~≰~nleftarrow~↚~nleftrightarrow~↮~nleq~≰~nleqq~≦̸~nleqslant~⩽̸~nles~⩽̸~nless~≮~nlsim~≴~nlt~≮~nltri~⋪~nltrie~⋬~nmid~∤~nopf~𝕟~notinE~⋹̸~notindot~⋵̸~notinva~∉~notinvb~⋷~notinvc~⋶~notni~∌~notniva~∌~notnivb~⋾~notnivc~⋽~npar~∦~nparallel~∦~nparsl~⫽⃥~npart~∂̸~npolint~⨔~npr~⊀~nprcue~⋠~npre~⪯̸~nprec~⊀~npreceq~⪯̸~nrArr~⇏~nrarr~↛~nrarrc~⤳̸~nrarrw~↝̸~nrightarrow~↛~nrtri~⋫~nrtrie~⋭~nsc~⊁~nsccue~⋡~nsce~⪰̸~nscr~𝓃~nshortmid~∤~nshortparallel~∦~nsim~≁~nsime~≄~nsimeq~≄~nsmid~∤~nspar~∦~nsqsube~⋢~nsqsupe~⋣~nsubE~⫅̸~nsube~⊈~nsubset~⊂⃒~nsubseteq~⊈~nsubseteqq~⫅̸~nsucc~⊁~nsucceq~⪰̸~nsup~⊅~nsupE~⫆̸~nsupe~⊉~nsupset~⊃⃒~nsupseteq~⊉~nsupseteqq~⫆̸~ntgl~≹~ntlg~≸~ntriangleleft~⋪~ntrianglelefteq~⋬~ntriangleright~⋫~ntrianglerighteq~⋭~num~#~numero~№~numsp~ ~nvDash~⊭~nvHarr~⤄~nvap~≍⃒~nvdash~⊬~nvge~≥⃒~nvgt~>⃒~nvinfin~⧞~nvlArr~⤂~nvle~≤⃒~nvlt~<⃒~nvltrie~⊴⃒~nvrArr~⤃~nvrtrie~⊵⃒~nvsim~∼⃒~nwArr~⇖~nwarhk~⤣~nwarr~↖~nwarrow~↖~nwnear~⤧~oS~Ⓢ~oast~⊛~ocir~⊚~ocy~о~odash~⊝~odblac~ő~odiv~⨸~odot~⊙~odsold~⦼~ofcir~⦿~ofr~𝔬~ogon~˛~ogt~⧁~ohbar~⦵~ohm~Ω~oint~∮~olarr~↺~olcir~⦾~olcross~⦻~olt~⧀~omacr~ō~omid~⦶~ominus~⊖~oopf~𝕠~opar~⦷~operp~⦹~orarr~↻~ord~⩝~order~ℴ~orderof~ℴ~origof~⊶~oror~⩖~orslope~⩗~orv~⩛~oscr~ℴ~osol~⊘~otimesas~⨶~ovbar~⌽~par~∥~parallel~∥~parsim~⫳~parsl~⫽~pcy~п~percnt~%~period~.~pertenk~‱~pfr~𝔭~phiv~ϕ~phmmat~ℳ~phone~☎~pitchfork~⋔~planck~ℏ~planckh~ℎ~plankv~ℏ~plus~+~plusacir~⨣~plusb~⊞~pluscir~⨢~plusdo~∔~plusdu~⨥~pluse~⩲~plussim~⨦~plustwo~⨧~pm~±~pointint~⨕~popf~𝕡~pr~≺~prE~⪳~prap~⪷~prcue~≼~pre~⪯~prec~≺~precapprox~⪷~preccurlyeq~≼~preceq~⪯~precnapprox~⪹~precneqq~⪵~precnsim~⋨~precsim~≾~primes~ℙ~prnE~⪵~prnap~⪹~prnsim~⋨~profalar~⌮~profline~⌒~profsurf~⌓~propto~∝~prsim~≾~prurel~⊰~pscr~𝓅~puncsp~ ~qfr~𝔮~qint~⨌~qopf~𝕢~qprime~⁗~qscr~𝓆~quaternions~ℍ~quatint~⨖~quest~?~questeq~≟~rAarr~⇛~rAtail~⤜~rBarr~⤏~rHar~⥤~race~∽̱~racute~ŕ~raemptyv~⦳~rangd~⦒~range~⦥~rangle~⟩~rarrap~⥵~rarrb~⇥~rarrbfs~⤠~rarrc~⤳~rarrfs~⤞~rarrhk~↪~rarrlp~↬~rarrpl~⥅~rarrsim~⥴~rarrtl~↣~rarrw~↝~ratail~⤚~ratio~∶~rationals~ℚ~rbarr~⤍~rbbrk~❳~rbrace~}~rbrack~]~rbrke~⦌~rbrksld~⦎~rbrkslu~⦐~rcaron~ř~rcedil~ŗ~rcub~}~rcy~р~rdca~⤷~rdldhar~⥩~rdquor~”~rdsh~↳~realine~ℛ~realpart~ℜ~reals~ℝ~rect~▭~rfisht~⥽~rfr~𝔯~rhard~⇁~rharu~⇀~rharul~⥬~rhov~ϱ~rightarrow~→~rightarrowtail~↣~rightharpoondown~⇁~rightharpoonup~⇀~rightleftarrows~⇄~rightleftharpoons~⇌~rightrightarrows~⇉~rightsquigarrow~↝~rightthreetimes~⋌~ring~˚~risingdotseq~≓~rlarr~⇄~rlhar~⇌~rmoust~⎱~rmoustache~⎱~rnmid~⫮~roang~⟭~roarr~⇾~robrk~⟧~ropar~⦆~ropf~𝕣~roplus~⨮~rotimes~⨵~rpar~)~rpargt~⦔~rppolint~⨒~rrarr~⇉~rscr~𝓇~rsh~↱~rsqb~]~rsquor~’~rthree~⋌~rtimes~⋊~rtri~▹~rtrie~⊵~rtrif~▸~rtriltri~⧎~ruluhar~⥨~rx~℞~sacute~ś~sc~≻~scE~⪴~scap~⪸~sccue~≽~sce~⪰~scedil~ş~scirc~ŝ~scnE~⪶~scnap~⪺~scnsim~⋩~scpolint~⨓~scsim~≿~scy~с~sdotb~⊡~sdote~⩦~seArr~⇘~searhk~⤥~searr~↘~searrow~↘~semi~;~seswar~⤩~setminus~∖~setmn~∖~sext~✶~sfr~𝔰~sfrown~⌢~sharp~♯~shchcy~щ~shcy~ш~shortmid~∣~shortparallel~∥~sigmav~ς~simdot~⩪~sime~≃~simeq~≃~simg~⪞~simgE~⪠~siml~⪝~simlE~⪟~simne~≆~simplus~⨤~simrarr~⥲~slarr~←~smallsetminus~∖~smashp~⨳~smeparsl~⧤~smid~∣~smile~⌣~smt~⪪~smte~⪬~smtes~⪬︀~softcy~ь~sol~/~solb~⧄~solbar~⌿~sopf~𝕤~spadesuit~♠~spar~∥~sqcap~⊓~sqcaps~⊓︀~sqcup~⊔~sqcups~⊔︀~sqsub~⊏~sqsube~⊑~sqsubset~⊏~sqsubseteq~⊑~sqsup~⊐~sqsupe~⊒~sqsupset~⊐~sqsupseteq~⊒~squ~□~square~□~squarf~▪~squf~▪~srarr~→~sscr~𝓈~ssetmn~∖~ssmile~⌣~sstarf~⋆~star~☆~starf~★~straightepsilon~ϵ~straightphi~ϕ~strns~¯~subE~⫅~subdot~⪽~subedot~⫃~submult~⫁~subnE~⫋~subne~⊊~subplus~⪿~subrarr~⥹~subset~⊂~subseteq~⊆~subseteqq~⫅~subsetneq~⊊~subsetneqq~⫋~subsim~⫇~subsub~⫕~subsup~⫓~succ~≻~succapprox~⪸~succcurlyeq~≽~succeq~⪰~succnapprox~⪺~succneqq~⪶~succnsim~⋩~succsim~≿~sung~♪~supE~⫆~supdot~⪾~supdsub~⫘~supedot~⫄~suphsol~⟉~suphsub~⫗~suplarr~⥻~supmult~⫂~supnE~⫌~supne~⊋~supplus~⫀~supset~⊃~supseteq~⊇~supseteqq~⫆~supsetneq~⊋~supsetneqq~⫌~supsim~⫈~supsub~⫔~supsup~⫖~swArr~⇙~swarhk~⤦~swarr~↙~swarrow~↙~swnwar~⤪~target~⌖~tbrk~⎴~tcaron~ť~tcedil~ţ~tcy~т~tdot~⃛~telrec~⌕~tfr~𝔱~therefore~∴~thetav~ϑ~thickapprox~≈~thicksim~∼~thkap~≈~thksim~∼~timesb~⊠~timesbar~⨱~timesd~⨰~tint~∭~toea~⤨~top~⊤~topbot~⌶~topcir~⫱~topf~𝕥~topfork~⫚~tosa~⤩~tprime~‴~triangle~▵~triangledown~▿~triangleleft~◃~trianglelefteq~⊴~triangleq~≜~triangleright~▹~trianglerighteq~⊵~tridot~◬~trie~≜~triminus~⨺~triplus~⨹~trisb~⧍~tritime~⨻~trpezium~⏢~tscr~𝓉~tscy~ц~tshcy~ћ~tstrok~ŧ~twixt~≬~twoheadleftarrow~↞~twoheadrightarrow~↠~uHar~⥣~ubrcy~ў~ubreve~ŭ~ucy~у~udarr~⇅~udblac~ű~udhar~⥮~ufisht~⥾~ufr~𝔲~uharl~↿~uharr~↾~uhblk~▀~ulcorn~⌜~ulcorner~⌜~ulcrop~⌏~ultri~◸~umacr~ū~uogon~ų~uopf~𝕦~uparrow~↑~updownarrow~↕~upharpoonleft~↿~upharpoonright~↾~uplus~⊎~upsi~υ~upuparrows~⇈~urcorn~⌝~urcorner~⌝~urcrop~⌎~uring~ů~urtri~◹~uscr~𝓊~utdot~⋰~utilde~ũ~utri~▵~utrif~▴~uuarr~⇈~uwangle~⦧~vArr~⇕~vBar~⫨~vBarv~⫩~vDash~⊨~vangrt~⦜~varepsilon~ϵ~varkappa~ϰ~varnothing~∅~varphi~ϕ~varpi~ϖ~varpropto~∝~varr~↕~varrho~ϱ~varsigma~ς~varsubsetneq~⊊︀~varsubsetneqq~⫋︀~varsupsetneq~⊋︀~varsupsetneqq~⫌︀~vartheta~ϑ~vartriangleleft~⊲~vartriangleright~⊳~vcy~в~vdash~⊢~vee~∨~veebar~⊻~veeeq~≚~vellip~⋮~verbar~|~vert~|~vfr~𝔳~vltri~⊲~vnsub~⊂⃒~vnsup~⊃⃒~vopf~𝕧~vprop~∝~vrtri~⊳~vscr~𝓋~vsubnE~⫋︀~vsubne~⊊︀~vsupnE~⫌︀~vsupne~⊋︀~vzigzag~⦚~wcirc~ŵ~wedbar~⩟~wedge~∧~wedgeq~≙~wfr~𝔴~wopf~𝕨~wp~℘~wr~≀~wreath~≀~wscr~𝓌~xcap~⋂~xcirc~◯~xcup~⋃~xdtri~▽~xfr~𝔵~xhArr~⟺~xharr~⟷~xlArr~⟸~xlarr~⟵~xmap~⟼~xnis~⋻~xodot~⨀~xopf~𝕩~xoplus~⨁~xotime~⨂~xrArr~⟹~xrarr~⟶~xscr~𝓍~xsqcup~⨆~xuplus~⨄~xutri~△~xvee~⋁~xwedge~⋀~yacy~я~ycirc~ŷ~ycy~ы~yfr~𝔶~yicy~ї~yopf~𝕪~yscr~𝓎~yucy~ю~zacute~ź~zcaron~ž~zcy~з~zdot~ż~zeetrf~ℨ~zfr~𝔷~zhcy~ж~zigrarr~⇝~zopf~𝕫~zscr~𝓏~~AMP~&~COPY~©~GT~>~LT~<~QUOT~\"~REG~®", namedReferences['html4']); 49 | -------------------------------------------------------------------------------- /src/numeric-unicode-map.ts: -------------------------------------------------------------------------------- 1 | export const numericUnicodeMap: Record = { 2 | 0: 65533, 3 | 128: 8364, 4 | 130: 8218, 5 | 131: 402, 6 | 132: 8222, 7 | 133: 8230, 8 | 134: 8224, 9 | 135: 8225, 10 | 136: 710, 11 | 137: 8240, 12 | 138: 352, 13 | 139: 8249, 14 | 140: 338, 15 | 142: 381, 16 | 145: 8216, 17 | 146: 8217, 18 | 147: 8220, 19 | 148: 8221, 20 | 149: 8226, 21 | 150: 8211, 22 | 151: 8212, 23 | 152: 732, 24 | 153: 8482, 25 | 154: 353, 26 | 155: 8250, 27 | 156: 339, 28 | 158: 382, 29 | 159: 376 30 | }; 31 | -------------------------------------------------------------------------------- /src/surrogate-pairs.ts: -------------------------------------------------------------------------------- 1 | export const fromCodePoint = 2 | String.fromCodePoint || 3 | function (astralCodePoint: number) { 4 | return String.fromCharCode( 5 | Math.floor((astralCodePoint - 0x10000) / 0x400) + 0xd800, 6 | ((astralCodePoint - 0x10000) % 0x400) + 0xdc00 7 | ); 8 | }; 9 | 10 | // @ts-expect-error - String.prototype.codePointAt might not exist in older node versions 11 | export const getCodePoint = String.prototype.codePointAt 12 | ? function (input: string, position: number) { 13 | return input.codePointAt(position); 14 | } 15 | : function (input: string, position: number) { 16 | return (input.charCodeAt(position) - 0xd800) * 0x400 + input.charCodeAt(position + 1) - 0xdc00 + 0x10000; 17 | }; 18 | 19 | export const highSurrogateFrom = 0xd800; 20 | export const highSurrogateTo = 0xdbff; 21 | -------------------------------------------------------------------------------- /test/index.test.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | import {exec as execOriginal} from 'node:child_process'; 3 | import * as fs from 'node:fs/promises'; 4 | import * as path from 'node:path'; 5 | import {promisify} from 'node:util'; 6 | import * as HtmlEntities from '../src/index.js'; 7 | import {namedReferences} from '../src/named-references'; 8 | 9 | const exec = promisify(execOriginal); 10 | 11 | async function ensureSelfLink(appPath: string) { 12 | const nodeModulesPath = path.join(appPath, 'node_modules'); 13 | const selfLinkPath = path.join(nodeModulesPath, 'html-entities'); 14 | try { 15 | await fs.mkdir(nodeModulesPath); 16 | } catch { 17 | // ignore 18 | } 19 | try { 20 | await fs.symlink('../../../..', selfLinkPath, 'dir'); 21 | } catch { 22 | // ignore 23 | } 24 | } 25 | 26 | describe('package', () => { 27 | it('should have no dependencies', async () => { 28 | expect(JSON.parse(await fs.readFile('package.json', 'utf8')).dependencies).toEqual(undefined); 29 | }); 30 | }); 31 | describe('lib', () => { 32 | let {encode, decode, decodeEntity} = {} as typeof HtmlEntities; 33 | beforeAll(async () => { 34 | if (process.env.TEST_DIST === 'esm') { 35 | ({encode, decode, decodeEntity} = await import('../dist/esm/index.js')); 36 | } else if (process.env.TEST_DIST === 'commonjs') { 37 | ({encode, decode, decodeEntity} = require('../dist/commonjs/index.js')); 38 | } else { 39 | ({encode, decode, decodeEntity} = await import('../src/index.js')); 40 | } 41 | }); 42 | if (process.env.TEST_DIST) { 43 | describe(`dist check (${process.env.TEST_DIST})`, () => { 44 | it(`should check if module type is set correctly ${process.env.TEST_DIST}`, async () => { 45 | if (process.env.TEST_DIST === 'esm') { 46 | await import('../dist/esm/index.js'); 47 | } else if (process.env.TEST_DIST === 'commonjs') { 48 | require('../dist/commonjs/index.js'); 49 | } 50 | }); 51 | it('should check integration app/package of the type', async () => { 52 | const appPath = `test/test-apps/${process.env.TEST_DIST}`; 53 | await ensureSelfLink(appPath); 54 | await exec(`node ${path.join(appPath, 'test.js')}`); 55 | }); 56 | }); 57 | } else { 58 | it('should have proper TS types', async () => { 59 | const appPath = `test/test-apps/ts`; 60 | await ensureSelfLink(appPath); 61 | await exec(`tsx ${path.join(appPath, 'test.ts')}`); 62 | }); 63 | } 64 | describe('encode()', () => { 65 | it('should handle undefined', () => { 66 | expect(decode(undefined)).toEqual(''); 67 | }); 68 | it('should handle null', () => { 69 | expect(decode(null)).toEqual(''); 70 | }); 71 | it('should handle empty string', () => { 72 | expect(encode('')).toEqual(''); 73 | }); 74 | it('should handle numbers (backwards compatibility)', () => { 75 | expect(encode(1 as unknown as string)).toEqual('1'); 76 | }); 77 | describe('mode', () => { 78 | it('should only match necessary entities', () => { 79 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'specialChars'})).toEqual( 80 | 'a\n<>"'&©∆℞😂\0\x01' 81 | ); 82 | expect(encode('a\n<>"\'&©∆℞😂\0\x01END', {mode: 'specialChars'})).toEqual( 83 | 'a\n<>"'&©∆℞😂\0\x01END' 84 | ); 85 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAscii'})).toEqual( 86 | 'a\n<>"'&©∆℞😂\0\x01' 87 | ); 88 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable'})).toEqual( 89 | 'a\n<>"'&©∆℞😂\0' 90 | ); 91 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintableOnly'})).toEqual( 92 | 'a\n<>"\'&©∆℞😂\0' 93 | ); 94 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'extensive'})).toEqual( 95 | 'a <>"'&©∆℞😂\0' 96 | ); 97 | }); 98 | }); 99 | describe('level', () => { 100 | it('should encode according to the level', () => { 101 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'all'})).toEqual( 102 | 'a\n<>"'&©∆℞😂\0' 103 | ); 104 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'html5'})).toEqual( 105 | 'a\n<>"'&©∆℞😂\0' 106 | ); 107 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'html4'})).toEqual( 108 | 'a\n<>"'&©∆℞😂\0' 109 | ); 110 | expect(encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'xml'})).toEqual( 111 | 'a\n<>"'&©∆℞😂\0' 112 | ); 113 | }); 114 | }); 115 | describe('numeric', () => { 116 | it('should use specified numeric style', () => { 117 | expect( 118 | encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'xml', numeric: 'decimal'}) 119 | ).toEqual('a\n<>"'&©∆℞😂\0'); 120 | expect( 121 | encode('a\n<>"\'&©∆℞😂\0\x01', {mode: 'nonAsciiPrintable', level: 'xml', numeric: 'hexadecimal'}) 122 | ).toEqual('a\n<>"'&©∆℞😂\0'); 123 | }); 124 | }); 125 | }); 126 | 127 | describe('decode()', () => { 128 | it('should handle undefined', () => { 129 | expect(decode(undefined)).toEqual(''); 130 | }); 131 | it('should handle null', () => { 132 | expect(decode(null)).toEqual(''); 133 | }); 134 | it('should handle empty string', () => { 135 | expect(decode('')).toEqual(''); 136 | }); 137 | it('should handle single ampersand', () => { 138 | expect(decode('&')).toEqual('&'); 139 | }); 140 | it('should handle incomplete entity', () => { 141 | expect(decode('&a')).toEqual('&a'); 142 | }); 143 | it('should handle invalid numeric entities', () => { 144 | expect(decode('�')).toEqual(String.fromCharCode(65533)); 145 | }); 146 | it('should decode numeric entities without semicolon', () => { 147 | expect(decode('"C"')).toEqual('"C"'); 148 | }); 149 | it('should decode incomplete named entities followed by alphanumeric characters', () => { 150 | expect(decode('über')).toEqual('über'); 151 | }); 152 | describe('level', () => { 153 | it('should decode according to the level', () => { 154 | expect(decode('a\n<>"'&©∆℞😂�', {level: 'all'})).toEqual( 155 | 'a\n<>"\'&©∆℞😂\uFFFD\x01' 156 | ); 157 | expect(decode('a\n<>"'&©∆℞😂�', {level: 'html5'})).toEqual( 158 | 'a\n<>"\'&©∆℞😂\uFFFD\x01' 159 | ); 160 | expect(decode('a\n<>"'&©∆℞😂�', {level: 'html4'})).toEqual( 161 | 'a\n<>"\'&©∆℞😂\uFFFD\x01' 162 | ); 163 | expect(decode('a\n<>"'&©∆℞😂�', {level: 'xml'})).toEqual( 164 | 'a\n<>"\'&©∆℞😂\uFFFD\x01' 165 | ); 166 | }); 167 | }); 168 | describe('scope', () => { 169 | it('should decode according to the scope', () => { 170 | expect(decode('&=123&lang=en&,&', {scope: 'strict'})).toEqual('&=123&lang=en&,&'); 171 | expect(decode('&=123&lang=en&,&', {scope: 'body'})).toEqual('&=123&lang=en&,&'); 172 | expect(decode('&=123&lang=en&,&', {scope: 'attribute'})).toEqual('&=123&lang=en&,&'); 173 | expect(decode('&=123', {scope: 'attribute'})).toEqual('&=123'); 174 | }); 175 | }); 176 | describe('bugs', () => { 177 | it('should properly process html5 entitites those names start with html4 entity name - #77', () => { 178 | for (const [entity, value] of Object.entries(namedReferences.html5.entities)) { 179 | expect(decode(entity)).toEqual(value); 180 | } 181 | }); 182 | }); 183 | }); 184 | 185 | describe('decodeEntity()', () => { 186 | it('should handle undefined', () => { 187 | expect(decodeEntity(undefined)).toEqual(''); 188 | }); 189 | it('should handle null', () => { 190 | expect(decodeEntity(null)).toEqual(''); 191 | }); 192 | it('should handle empty string', () => { 193 | expect(decodeEntity('')).toEqual(''); 194 | }); 195 | it('should handle null-char', () => { 196 | expect(decodeEntity('�')).toEqual(String.fromCharCode(65533)); 197 | }); 198 | it('should handle hex entities', () => { 199 | expect(decodeEntity('ആ')).toEqual('ആ'); 200 | expect(decodeEntity('ആ')).toEqual('ആ'); 201 | }); 202 | it('should handle invalid numeric entities', () => { 203 | expect(decodeEntity('�')).toEqual(String.fromCharCode(65533)); 204 | }); 205 | it('should decode numeric entities without semicolon', () => { 206 | expect(decodeEntity('"')).toEqual('"'); 207 | }); 208 | it('should decode incomplete named entities', () => { 209 | expect(decodeEntity('ü')).toEqual('ü'); 210 | }); 211 | it('should decode proper named entities', () => { 212 | expect(decodeEntity('&')).toEqual('&'); 213 | expect(decodeEntity('&')).toEqual('&'); 214 | expect(decodeEntity('&=')).toEqual('&='); 215 | }); 216 | it('should decode emoji', () => { 217 | expect(decodeEntity('😂')).toEqual('😂'); 218 | }); 219 | describe('level', () => { 220 | it('should decode according to the level', () => { 221 | expect(decodeEntity('℞', {level: 'all'})).toEqual('℞'); 222 | expect(decodeEntity('℞', {level: 'html5'})).toEqual('℞'); 223 | expect(decodeEntity('℞', {level: 'html4'})).toEqual('℞'); 224 | expect(decodeEntity('©', {level: 'html4'})).toEqual('©'); 225 | expect(decodeEntity('℞', {level: 'xml'})).toEqual('℞'); 226 | expect(decodeEntity('©', {level: 'xml'})).toEqual('©'); 227 | expect(decodeEntity('<', {level: 'xml'})).toEqual('<'); 228 | }); 229 | }); 230 | }); 231 | }); 232 | -------------------------------------------------------------------------------- /test/test-apps/commonjs/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} 2 | -------------------------------------------------------------------------------- /test/test-apps/commonjs/test.js: -------------------------------------------------------------------------------- 1 | /* eslint-disable @typescript-eslint/no-require-imports */ 2 | const assert = require('node:assert'); 3 | const {encode, decode, decodeEntity} = require('html-entities'); 4 | 5 | assert.equal('<>&"'', encode('<>&"\'')); 6 | assert.equal('<>&"\'', decode('<>&"'')); 7 | assert.equal('<', decodeEntity('<')); 8 | 9 | console.log('OK'); 10 | -------------------------------------------------------------------------------- /test/test-apps/esm/package.json: -------------------------------------------------------------------------------- 1 | {"type": "module"} 2 | -------------------------------------------------------------------------------- /test/test-apps/esm/test.js: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert'; 2 | import {encode, decode, decodeEntity} from 'html-entities'; 3 | 4 | assert.equal('<>&"'', encode('<>&"\'')); 5 | assert.equal('<>&"\'', decode('<>&"'')); 6 | assert.equal('<', decodeEntity('<')); 7 | 8 | console.log('OK'); 9 | -------------------------------------------------------------------------------- /test/test-apps/ts/package.json: -------------------------------------------------------------------------------- 1 | {"type": "commonjs"} 2 | -------------------------------------------------------------------------------- /test/test-apps/ts/test.ts: -------------------------------------------------------------------------------- 1 | import * as assert from 'node:assert'; 2 | import {encode, decode, decodeEntity} from 'html-entities'; 3 | 4 | assert.equal('<>&"'', encode('<>&"\'')); 5 | assert.equal('<>&"\'', decode('<>&"'')); 6 | assert.equal('<', decodeEntity('<')); 7 | 8 | console.log('OK'); 9 | -------------------------------------------------------------------------------- /tools/process-named-references.ts: -------------------------------------------------------------------------------- 1 | import * as fs from 'fs'; 2 | import * as path from 'path'; 3 | 4 | export const pairDivider = '~'; 5 | export const blockDivider = '~~'; 6 | 7 | type NamedReferences = Record<'xml' | 'html4' | 'html5', Record> 11 | 12 | const getObjectEntries = Object.entries as (obj: T) => [keyof T, T[keyof T]][]; 13 | 14 | type Level = keyof typeof namedReferences; 15 | 16 | const result: {[key in Level]?: string} = {}; 17 | 18 | const regExpStart = '/'; 19 | const regExpEnd = '/g'; 20 | const regExpStartBody = '&(?:'; 21 | const regExpCommon = '#\\d+|#[xX][\\da-fA-F]+|[0-9a-zA-Z]+)'; 22 | const regExpEndBody = ';?'; 23 | 24 | function entitityPairsToString(input: Map) { 25 | return Array.from(input.entries()).map(([entity, characters]) => `${entity}${pairDivider}${characters}`).join(pairDivider); 26 | } 27 | 28 | const bodyRegExps: [string, string][] = []; 29 | const namedReferences = JSON.parse(fs.readFileSync('tools/named-references.source.json', 'utf-8')) as NamedReferences; 30 | const entitiesByLevel: Record> = {}; 31 | const processedEntities: Set = new Set(); 32 | for (const [level, entityInfos] of getObjectEntries(namedReferences)) { 33 | const conflictingBodyRegExpNamedReferences: string[] = []; 34 | const bodyRegExpNamedReferences: string[] = []; 35 | const entities: string[] = Object.keys(entityInfos); 36 | const strictEntities = new Map( 37 | Object.entries(entityInfos) 38 | .filter(([entity]) => !processedEntities.has(entity)) 39 | .map(([entity, {characters}]): readonly [string, string] => [entity.replace(/^&|;$/g, ''), characters]) 40 | ); 41 | entitiesByLevel[level] = {}; 42 | const optionalEntities = new Map(); 43 | for (const [entity, {characters}] of getObjectEntries(entityInfos)) { 44 | if (level !== 'xml' && !processedEntities.has(entity)) { 45 | processedEntities.add(entity); 46 | if (!entity.endsWith(';')) { 47 | const entityName = entity.slice(1); 48 | optionalEntities.set(entityName, characters); 49 | strictEntities.delete(entityName); 50 | } 51 | } 52 | entitiesByLevel[level][entity] = characters; 53 | if (!entity.endsWith(';')) { 54 | bodyRegExpNamedReferences.push(entity.slice(1)); 55 | } else { 56 | for (const otherEntity of entities) { 57 | if (otherEntity !== entity && otherEntity + ';' !== entity && entity.startsWith(otherEntity)) { 58 | conflictingBodyRegExpNamedReferences.push(entity); 59 | } 60 | } 61 | } 62 | } 63 | 64 | result[level] = [ 65 | entitityPairsToString(strictEntities), 66 | ...(optionalEntities.size > 0 ? [entitityPairsToString(optionalEntities)] : []), 67 | ].join(blockDivider); 68 | bodyRegExpNamedReferences.push(regExpCommon); 69 | bodyRegExps.push([ 70 | level, 71 | regExpStart + 72 | conflictingBodyRegExpNamedReferences 73 | .concat(regExpStartBody + bodyRegExpNamedReferences.join('|') + regExpEndBody) 74 | .join('|') + 75 | regExpEnd 76 | ]); 77 | } 78 | 79 | const processedNamedReferences = `// This file is autogenerated by tools/process-named-references.ts 80 | const pairDivider = ${JSON.stringify(pairDivider)}; 81 | const blockDivider = ${JSON.stringify(blockDivider)}; 82 | 83 | type GeneratedReferences = { 84 | entities: Record; 85 | characters: Record; 86 | }; 87 | 88 | function generateNamedReferences(input: string, prev?: GeneratedReferences): GeneratedReferences { 89 | const entities: Record = {}; 90 | const characters: Record = {}; 91 | const blocks = input.split(blockDivider); 92 | let isOptionalBlock = false; 93 | for (let i = 0; blocks.length > i; i++) { 94 | const entries = blocks[i].split(pairDivider); 95 | for (let j = 0; j < entries.length; j+=2) { 96 | const entity = entries[j]; 97 | const character = entries[j+1]; 98 | const fullEntity = '&' + entity + ';'; 99 | entities[fullEntity] = character; 100 | if (isOptionalBlock) { 101 | entities['&' + entity] = character; 102 | } 103 | characters[character] = fullEntity; 104 | } 105 | isOptionalBlock = true; 106 | } 107 | return prev ? 108 | {entities: {...entities, ...prev.entities}, characters: {...characters, ...prev.characters}} : 109 | {entities, characters}; 110 | } 111 | 112 | export type NamedReferences = { 113 | [K in ${Object.keys(result) 114 | .map((level) => `'${level}'`) 115 | .join(' | ')}]: { 116 | entities: Record; 117 | characters: Record; 118 | } 119 | }; 120 | export const bodyRegExps = { 121 | ${bodyRegExps.map(([level, regExpStart]) => `${level}: ${regExpStart}`).join(',\n ')} 122 | }; 123 | export const namedReferences = {} as NamedReferences; 124 | ${ 125 | Object.entries(result) 126 | .map(([level, data], index, all) => `namedReferences['${level}'] = generateNamedReferences(${JSON.stringify(data)}${ 127 | index > 1 ? `, namedReferences['${all[index - 1][0]}']` : '' 128 | });`) 129 | .join('\n') 130 | } 131 | `; 132 | 133 | fs.writeFileSync(path.join(__dirname, '..', 'src', 'named-references.ts'), processedNamedReferences); 134 | -------------------------------------------------------------------------------- /tsconfig.esm.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "module": "esnext", 5 | "target": "esnext", 6 | "declaration": true, 7 | "skipLibCheck": true, 8 | "outDir": "./dist/esm" 9 | }, 10 | } 11 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "commonjs", 5 | "outDir": "./dist/commonjs", 6 | "strict": true, 7 | "sourceMap": true 8 | }, 9 | "include": ["src"], 10 | "exclude": ["node_modules", "test"] 11 | } 12 | --------------------------------------------------------------------------------