unicode-input-toolconverter 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.babelrc.json +3 -0
- package/.editorconfig +16 -0
- package/.nojekyll +0 -0
- package/.nyc_output/out.json +173804 -0
- package/.nyc_output/processinfo/index.json +1 -0
- package/CHANGES.md +11 -0
- package/COPYING.LESSER.txt +180 -0
- package/COPYING.txt +687 -0
- package/README.md +65 -0
- package/_locales/en-US/messages.json +3126 -0
- package/_locales/hu-HU/messages.json +3126 -0
- package/_locales/pt-BR/messages.json +3126 -0
- package/_locales/sv-SE/messages.json +3126 -0
- package/babel.config.json +5 -0
- package/browser_action/characterSelection.js +91 -0
- package/browser_action/charrefConverters.js +389 -0
- package/browser_action/chartBuild.js +155 -0
- package/browser_action/encodingBehaviors.js +72 -0
- package/browser_action/encodings.js +97 -0
- package/browser_action/entities.js +12 -0
- package/browser_action/entityBehaviors.js +96 -0
- package/browser_action/index-es.html +15 -0
- package/browser_action/index-instrumented.html +16 -0
- package/browser_action/index-pages.html +16 -0
- package/browser_action/index.html +16 -0
- package/browser_action/index.iife.min.js +2 -0
- package/browser_action/index.iife.min.js.map +1 -0
- package/browser_action/index.instrumented.iife.min.js +2 -0
- package/browser_action/index.instrumented.iife.min.js.map +1 -0
- package/browser_action/index.js +95 -0
- package/browser_action/preferences/prefDefaults.js +52 -0
- package/browser_action/service-worker/sw-activateCallback.js +53 -0
- package/browser_action/service-worker/sw-locales.json +6 -0
- package/browser_action/service-worker/sw-resources.json +73 -0
- package/browser_action/service-worker/sw-unicode-data.json +3 -0
- package/browser_action/styles/unicode-dialog.css +391 -0
- package/browser_action/templateUtils/elements.js +12 -0
- package/browser_action/templateUtils/fill.js +3 -0
- package/browser_action/templateUtils/validation.js +6 -0
- package/browser_action/templates/chartBuild.js +185 -0
- package/browser_action/templates/index.js +1304 -0
- package/browser_action/templatesElementCustomization/widgets.js +56 -0
- package/browser_action/unicode/UnicodeConverter.js +793 -0
- package/browser_action/unicode/charrefunicodeDb.js +258 -0
- package/browser_action/unicode/getScriptInfoForCodePoint.js +646 -0
- package/browser_action/unicode/hangul.js +253 -0
- package/browser_action/unicode/lastScriptNames.json +5 -0
- package/browser_action/unicode/parseUnihanFromTextFileStrings.js +56 -0
- package/browser_action/unicode/unicodeFieldInfo.js +120 -0
- package/browser_action/unicode/unicodeScripts.js +4912 -0
- package/browser_action/unicode/unihan.js +129 -0
- package/browser_action/unicode/unihanDbPopulate.js +26 -0
- package/browser_action/unicodecharref.js +1480 -0
- package/browser_action/utils/DOMUtils.js +122 -0
- package/browser_action/utils/FetchUtils.js +19 -0
- package/browser_action/utils/TextUtils.js +28 -0
- package/browser_action/utils/TypedArrayUtils.js +21 -0
- package/browser_action/utils/semicolonSeparatedToArray.js +12 -0
- package/browser_action/utils/setupServiceWorker.js +193 -0
- package/cypress.config.js +21 -0
- package/download/entities/copyright-software-20021231.html +70 -0
- package/download/entities/html5-uppercase.ent +54 -0
- package/download/entities/htmlmathml-f.ent +2164 -0
- package/download/entities/isoamsa.ent +201 -0
- package/download/entities/isoamsb.ent +177 -0
- package/download/entities/isoamsc.ent +77 -0
- package/download/entities/isoamsn.ent +148 -0
- package/download/entities/isoamso.ent +107 -0
- package/download/entities/isoamsr.ent +238 -0
- package/download/entities/isobox.ent +95 -0
- package/download/entities/isocyr1.ent +122 -0
- package/download/entities/isocyr2.ent +81 -0
- package/download/entities/isodia.ent +69 -0
- package/download/entities/isogrk1.ent +104 -0
- package/download/entities/isogrk2.ent +75 -0
- package/download/entities/isogrk3.ent +98 -0
- package/download/entities/isogrk4.ent +98 -0
- package/download/entities/isolat1.ent +117 -0
- package/download/entities/isolat2.ent +176 -0
- package/download/entities/isomfrk.ent +107 -0
- package/download/entities/isomopf.ent +81 -0
- package/download/entities/isomscr.ent +107 -0
- package/download/entities/isonum.ent +131 -0
- package/download/entities/isopub.ent +140 -0
- package/download/entities/isotech.ent +216 -0
- package/download/entities/mmlalias.ent +598 -0
- package/download/entities/mmlextra.ent +154 -0
- package/download/entities/predefined.ent +52 -0
- package/download/entities/w3centities-f.ent +2276 -0
- package/download/entities/xhtml1-lat1.ent +143 -0
- package/download/entities/xhtml1-special.ent +78 -0
- package/download/entities/xhtml1-symbol.ent +171 -0
- package/eslint.config.js +66 -0
- package/icons/openWindow16.png +0 -0
- package/icons/openWindow24.png +0 -0
- package/lgtm.yml +5 -0
- package/lib/background.html +11 -0
- package/lib/background.js +13 -0
- package/lib/overlay.css +6 -0
- package/manifest.json +37 -0
- package/package.json +121 -0
- package/polyfills/browser-polyfill.min.js +8 -0
- package/polyfills/browser-polyfill.min.js.map +1 -0
- package/server.js +61 -0
- package/sw.js +259 -0
- package/tools/entities-import.js +62 -0
- package/tools/findEsResources.js +30 -0
- package/tools/list-locales.js +17 -0
- package/tools/parseUnicodeCharts.js +428 -0
- package/tools/ucd-import.js +24 -0
- package/tools/unicode-charts.html +596 -0
- package/tools/unihan-import.js +72 -0
- package/unicode_copyright.txt +46 -0
- package/vendor/camelcase/index.js +110 -0
- package/vendor/fflate/esm/browser.js +2665 -0
- package/vendor/intl-dom/dist/index.esm.js +2962 -0
- package/vendor/jamilih/dist/jml-es.js +2359 -0
- package/vendor/jquery/dist/jquery.js +10716 -0
- package/vendor/json-6/dist/index.mjs +1783 -0
- package/vendor/miller-columns/dist/index-es.min.js +1 -0
- package/vendor/miller-columns/miller-columns.css +75 -0
- package/vendor/simple-prefs/dist/index.esm.js +286 -0
- package/web-ext-config.cjs +13 -0
|
@@ -0,0 +1,793 @@
|
|
|
1
|
+
/* eslint-disable class-methods-use-this -- Todo: fix later */
|
|
2
|
+
import {getUnicodeDefaults} from '../preferences/prefDefaults.js';
|
|
3
|
+
import {getHangulName, getHangulFromName} from './hangul.js';
|
|
4
|
+
import charrefunicodeDb from './charrefunicodeDb.js';
|
|
5
|
+
import unicodecharref from '../unicodecharref.js';
|
|
6
|
+
import camelCase from '../../vendor/camelcase/index.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @typedef {"php"|"css"|"javascript"} UnicodeEscapeMode
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @namespace Converts from one string form to another
|
|
14
|
+
*/
|
|
15
|
+
const decim = /&#(\d*);/gu;
|
|
16
|
+
const hexadec = /&#[xX]([\da-fA-F]*);/gu;
|
|
17
|
+
|
|
18
|
+
const builtinEntities = new Set(['apos', 'quot', 'lt', 'gt', 'amp']);
|
|
19
|
+
|
|
20
|
+
// Per https://www.w3.org/TR/xml/#sec-suggested-names
|
|
21
|
+
// Continue also needs: "Characters for Natural Language Identifiers" in
|
|
22
|
+
// https://unicode.org/reports/tr31/ ;
|
|
23
|
+
// Currently appears to be Tables 3, 3a, and 3b
|
|
24
|
+
// (besides \u0027 and \u2019 per XML)
|
|
25
|
+
const xmlName = /[\p{ID_Start}_][\p{ID_Continue}\u0024\u005F\u002D\u002E\u003A\u00B7\u058A\u05F4\u0F0B\u200C\u2010\u2027\u30A0\u30FB\u05F3\u200D]*/gui;
|
|
26
|
+
// const htmlOrXmlEnt = /&([a-z\d]+);/gui; // Works for basic HTML entitites
|
|
27
|
+
const htmlOrXmlEnt = new RegExp('&(' + xmlName.source + ');', 'gui');
|
|
28
|
+
|
|
29
|
+
export const getUnicodeConverter = () => {
|
|
30
|
+
const {getPref} = getUnicodeDefaults();
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
*
|
|
34
|
+
*/
|
|
35
|
+
return class UnicodeConverter {
|
|
36
|
+
/**
|
|
37
|
+
* @param {IntlDom} _
|
|
38
|
+
*/
|
|
39
|
+
constructor ({_}) {
|
|
40
|
+
this._ = _;
|
|
41
|
+
this.newents = [];
|
|
42
|
+
this.newcharrefs = [];
|
|
43
|
+
|
|
44
|
+
this.entities = [];
|
|
45
|
+
this.numericCharacterReferences = [];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Ensure dynamic as array may change.
|
|
50
|
+
* @returns {Integer}
|
|
51
|
+
*/
|
|
52
|
+
getAposPos () {
|
|
53
|
+
return this.numericCharacterReferences.indexOf(39);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Ensure dynamic as array may change.
|
|
57
|
+
* @returns {Integer}
|
|
58
|
+
*/
|
|
59
|
+
getAmpPos () {
|
|
60
|
+
return this.numericCharacterReferences.indexOf(38);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* @param {string} out
|
|
65
|
+
* @returns {string}
|
|
66
|
+
*/
|
|
67
|
+
charref2unicodeval (out) {
|
|
68
|
+
out = out.replaceAll(decim, function (match, match1) {
|
|
69
|
+
return String.fromCodePoint(match1);
|
|
70
|
+
}).replaceAll(hexadec, function (match, match1) {
|
|
71
|
+
return String.fromCodePoint(Number.parseInt(match1, 16));
|
|
72
|
+
});
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {string} out
|
|
78
|
+
* @returns {string}
|
|
79
|
+
*/
|
|
80
|
+
async charref2htmlentsval (out) {
|
|
81
|
+
// If true, should allow conversion to '
|
|
82
|
+
const xhtmlentmode = await getPref('xhtmlentmode');
|
|
83
|
+
|
|
84
|
+
out = out.replaceAll(decim, (match, match1) => {
|
|
85
|
+
const matched = this.numericCharacterReferences.indexOf(
|
|
86
|
+
Number.parseInt(match1)
|
|
87
|
+
);
|
|
88
|
+
if (
|
|
89
|
+
matched !== -1 &&
|
|
90
|
+
(matched !== this.getAposPos() || xhtmlentmode)
|
|
91
|
+
) {
|
|
92
|
+
return '&' + this.entities[matched] + ';';
|
|
93
|
+
}
|
|
94
|
+
return match;
|
|
95
|
+
}).replaceAll(hexadec, (match, match1) => {
|
|
96
|
+
const matched = this.numericCharacterReferences.indexOf(
|
|
97
|
+
Number.parseInt('0x' + match1, 16)
|
|
98
|
+
);
|
|
99
|
+
if (
|
|
100
|
+
matched !== -1 && (
|
|
101
|
+
matched !== this.getAposPos() || xhtmlentmode
|
|
102
|
+
)
|
|
103
|
+
) {
|
|
104
|
+
return '&' + this.entities[matched] + ';';
|
|
105
|
+
}
|
|
106
|
+
return match;
|
|
107
|
+
});
|
|
108
|
+
return out;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {string} unicodeToConvert
|
|
113
|
+
* @param {boolean} leaveSurrogates
|
|
114
|
+
* @returns {string}
|
|
115
|
+
*/
|
|
116
|
+
async unicode2charrefDecval (unicodeToConvert, leaveSurrogates) {
|
|
117
|
+
let out = '';
|
|
118
|
+
const asciiLt128 = await getPref('asciiLt128');
|
|
119
|
+
for (let i = 0; i < unicodeToConvert.length; i++) {
|
|
120
|
+
let temp = unicodeToConvert.charCodeAt(i);
|
|
121
|
+
// Todo: Redo with `codePointAt`?
|
|
122
|
+
if (!leaveSurrogates && temp >= 0xD800 && temp < 0xF900) { // surrogate
|
|
123
|
+
temp = ((temp - 0xD800) * 0x400) +
|
|
124
|
+
(unicodeToConvert.charCodeAt(i + 1) - 0xDC00) + 0x10000;
|
|
125
|
+
// Could do test on temp.isNan() (e.g., if trying to convert
|
|
126
|
+
// a surrogate by itself in regular (non-surrogate converting) mode)
|
|
127
|
+
out += '&#' + temp + ';';
|
|
128
|
+
i++; // Skip the next surrogate
|
|
129
|
+
|
|
130
|
+
// Replace this 'if' condition and remove the 'else' if also
|
|
131
|
+
// want ascii
|
|
132
|
+
} else if (temp >= 128 || asciiLt128) {
|
|
133
|
+
out += '&#' + temp + ';';
|
|
134
|
+
} else {
|
|
135
|
+
out += unicodeToConvert.charAt(i);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
return out;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* @param {string} unicodeToConvert
|
|
143
|
+
* @param {boolean} leaveSurrogates
|
|
144
|
+
* @param {UnicodeEscapeMode} type
|
|
145
|
+
* @returns {string}
|
|
146
|
+
*/
|
|
147
|
+
async unicode2charrefHexval (unicodeToConvert, leaveSurrogates, type) {
|
|
148
|
+
// alert(unicodeToConvert + '::' + leaveSurrogates + '::' + type);
|
|
149
|
+
let out = '';
|
|
150
|
+
let xstyle, beginEscape, endEscape, cssUnambiguous;
|
|
151
|
+
// Fix: offer a U+.... option (similar to 'php' or 'javascript'
|
|
152
|
+
// depending on if length is desired as 4 or 6)
|
|
153
|
+
if (type === 'javascript' || type === 'php') {
|
|
154
|
+
xstyle = '';
|
|
155
|
+
beginEscape = String.raw`\u`;
|
|
156
|
+
endEscape = '';
|
|
157
|
+
} else if (type === 'css') {
|
|
158
|
+
cssUnambiguous = await getPref('cssUnambiguous');
|
|
159
|
+
xstyle = '';
|
|
160
|
+
beginEscape = '\\';
|
|
161
|
+
endEscape = cssUnambiguous ? '' : await getPref('cssWhitespace');
|
|
162
|
+
} else {
|
|
163
|
+
xstyle = 'x';
|
|
164
|
+
beginEscape = '&#';
|
|
165
|
+
endEscape = ';';
|
|
166
|
+
/*
|
|
167
|
+
if (!(await getPref('hexstyleLwr'))) {
|
|
168
|
+
xstyle = 'X';
|
|
169
|
+
}
|
|
170
|
+
*/
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const [hexLettersUpper, asciiLt128] = await Promise.all([
|
|
174
|
+
getPref('hexLettersUpper'),
|
|
175
|
+
getPref('asciiLt128')
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
for (let i = 0; i < unicodeToConvert.length; i++) {
|
|
179
|
+
let hexletters;
|
|
180
|
+
let temp = unicodeToConvert.charCodeAt(i);
|
|
181
|
+
// Todo: Redo with `codePointAt`?
|
|
182
|
+
if (!leaveSurrogates && temp >= 0xD800 && temp < 0xF900) { // surrogate
|
|
183
|
+
temp = ((temp - 0xD800) * 0x400) +
|
|
184
|
+
(unicodeToConvert.charCodeAt(i + 1) - 0xDC00) + 0x10000;
|
|
185
|
+
hexletters = temp.toString(16);
|
|
186
|
+
i++; // Skip the next surrogate
|
|
187
|
+
if (hexLettersUpper) {
|
|
188
|
+
hexletters = hexletters.toUpperCase();
|
|
189
|
+
}
|
|
190
|
+
if ((type === 'php' || cssUnambiguous) && hexletters.length < 6) {
|
|
191
|
+
hexletters = hexletters.padStart(6, '0');
|
|
192
|
+
}
|
|
193
|
+
out += beginEscape + xstyle + hexletters + endEscape;
|
|
194
|
+
// Replace this 'if' condition and remove the 'else' if also want ascii
|
|
195
|
+
} else if (temp >= 128 || asciiLt128) {
|
|
196
|
+
hexletters = temp.toString(16);
|
|
197
|
+
if (hexLettersUpper) {
|
|
198
|
+
hexletters = hexletters.toUpperCase();
|
|
199
|
+
}
|
|
200
|
+
if (type === 'javascript' && hexletters.length < 4) {
|
|
201
|
+
hexletters = hexletters.padStart(4, '0');
|
|
202
|
+
} else if (
|
|
203
|
+
(type === 'php' || cssUnambiguous) &&
|
|
204
|
+
hexletters.length < 6
|
|
205
|
+
) {
|
|
206
|
+
hexletters = hexletters.padStart(6, '0');
|
|
207
|
+
}
|
|
208
|
+
out += beginEscape + xstyle + hexletters + endEscape;
|
|
209
|
+
} else {
|
|
210
|
+
out += unicodeToConvert.charAt(i);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
return out;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* @param {string} unicodeToConvert
|
|
218
|
+
* @returns {string}
|
|
219
|
+
*/
|
|
220
|
+
async unicode2htmlentsval (unicodeToConvert) {
|
|
221
|
+
for (let i = 0; i < this.newents.length; i++) {
|
|
222
|
+
unicodeToConvert = unicodeToConvert.replaceAll(
|
|
223
|
+
this.newcharrefs[i], '&' + this.newents[i] + ';'
|
|
224
|
+
);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
let out = '';
|
|
228
|
+
// If true, should allow conversion to '
|
|
229
|
+
const xhtmlentmode = await getPref('xhtmlentmode');
|
|
230
|
+
// If true, will not convert '&' to '&'
|
|
231
|
+
const ampkeep = await getPref('ampkeep');
|
|
232
|
+
|
|
233
|
+
for (const ch of unicodeToConvert) {
|
|
234
|
+
const codePoint = ch.codePointAt();
|
|
235
|
+
const tempcharref = this.numericCharacterReferences.indexOf(
|
|
236
|
+
codePoint
|
|
237
|
+
);
|
|
238
|
+
|
|
239
|
+
out += tempcharref !== -1 &&
|
|
240
|
+
(xhtmlentmode || tempcharref !== this.getAposPos()) &&
|
|
241
|
+
(!ampkeep || tempcharref !== this.getAmpPos())
|
|
242
|
+
? '&' + this.entities[tempcharref] + ';'
|
|
243
|
+
: ch;
|
|
244
|
+
}
|
|
245
|
+
return out;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* @param {string} out
|
|
250
|
+
* @returns {string}
|
|
251
|
+
*/
|
|
252
|
+
async htmlents2charrefDecval (out) {
|
|
253
|
+
// If true, don't convert ', ", <, >, and &
|
|
254
|
+
const xmlentkeep = await getPref('xmlentkeep');
|
|
255
|
+
return out.replaceAll(htmlOrXmlEnt, (match, match1) => {
|
|
256
|
+
if (!xmlentkeep || !builtinEntities.has(match1)) {
|
|
257
|
+
// If recognized multiple char ent. (won't convert these to decimal)
|
|
258
|
+
if (this.newents.includes(match1)) {
|
|
259
|
+
return this.newcharrefs[this.newents.indexOf(match1)];
|
|
260
|
+
}
|
|
261
|
+
// If recognized single char. ent.
|
|
262
|
+
if (this.entities.includes(match1)) {
|
|
263
|
+
return '&#' + this.numericCharacterReferences[
|
|
264
|
+
this.entities.indexOf(match1)
|
|
265
|
+
] + ';';
|
|
266
|
+
}
|
|
267
|
+
// If unrecognized
|
|
268
|
+
return '&' + match1 + ';';
|
|
269
|
+
}
|
|
270
|
+
// If keeping predefined XML entities (and this is one)
|
|
271
|
+
return '&' + match1 + ';';
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/**
|
|
276
|
+
* @param {string} out
|
|
277
|
+
* @returns {string}
|
|
278
|
+
*/
|
|
279
|
+
async htmlents2charrefHexval (out) {
|
|
280
|
+
const xstyle = 'x';
|
|
281
|
+
/* if (!(await getPref('hexstyleLwr'))) {
|
|
282
|
+
xstyle = 'X';
|
|
283
|
+
} */
|
|
284
|
+
// If true, don't convert ', ", <, >, and &
|
|
285
|
+
const [xmlentkeep, hexLettersUpper] = await Promise.all([
|
|
286
|
+
getPref('xmlentkeep'),
|
|
287
|
+
getPref('hexLettersUpper')
|
|
288
|
+
]);
|
|
289
|
+
return out.replaceAll(htmlOrXmlEnt, (match, match1) => {
|
|
290
|
+
if (!xmlentkeep || !builtinEntities.has(match1)) {
|
|
291
|
+
const b = this.numericCharacterReferences[
|
|
292
|
+
this.entities.indexOf(match1)
|
|
293
|
+
];
|
|
294
|
+
const c = this.newents.indexOf(match1);
|
|
295
|
+
|
|
296
|
+
// If recognized multiple char. ent. (won't convert these to
|
|
297
|
+
// hexadecimal)
|
|
298
|
+
if (c !== -1) {
|
|
299
|
+
return this.newcharrefs[c];
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// If recognized single char. ent.
|
|
303
|
+
if (this.entities.includes(match1)) {
|
|
304
|
+
let hexletters = b.toString(16);
|
|
305
|
+
if (hexLettersUpper) {
|
|
306
|
+
hexletters = hexletters.toUpperCase();
|
|
307
|
+
}
|
|
308
|
+
return '&#' + xstyle + hexletters + ';';
|
|
309
|
+
} // If unrecognized ent.
|
|
310
|
+
return '&' + match1 + ';';
|
|
311
|
+
}
|
|
312
|
+
// If keeping predefined XML entities (and this is one)
|
|
313
|
+
return '&' + match1 + ';';
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* @param {string} out
|
|
319
|
+
* @returns {string}
|
|
320
|
+
*/
|
|
321
|
+
async htmlents2unicodeval (out) {
|
|
322
|
+
// If true, don't convert ', ", <, >, and &
|
|
323
|
+
const xmlentkeep = await getPref('xmlentkeep');
|
|
324
|
+
return out.replaceAll(htmlOrXmlEnt, (match, match1) => {
|
|
325
|
+
if (!xmlentkeep || !builtinEntities.has(match1)) {
|
|
326
|
+
const b = this.numericCharacterReferences[
|
|
327
|
+
this.entities.indexOf(match1)
|
|
328
|
+
];
|
|
329
|
+
|
|
330
|
+
// If recognized multiple char ent.
|
|
331
|
+
if (this.newents.includes(match1)) {
|
|
332
|
+
return this.newcharrefs[this.newents.indexOf(match1)];
|
|
333
|
+
}
|
|
334
|
+
// If recognized single char. ent.
|
|
335
|
+
if (this.entities.includes(match1)) {
|
|
336
|
+
return String.fromCodePoint(b);
|
|
337
|
+
}
|
|
338
|
+
// If unrecognized
|
|
339
|
+
return '&' + match1 + ';';
|
|
340
|
+
}
|
|
341
|
+
// If keeping predefined XML entities (and this is one)
|
|
342
|
+
return '&' + match1 + ';';
|
|
343
|
+
});
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
/**
|
|
347
|
+
* @param {string} out
|
|
348
|
+
* @returns {string}
|
|
349
|
+
*/
|
|
350
|
+
hex2decval (out) {
|
|
351
|
+
return out.replaceAll(hexadec, function (match, match1) {
|
|
352
|
+
return '&#' + Number.parseInt(match1, 16) + ';';
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* @param {string} out
|
|
358
|
+
* @returns {string}
|
|
359
|
+
*/
|
|
360
|
+
async dec2hexval (out) {
|
|
361
|
+
const xstyle = 'x';
|
|
362
|
+
/* if (!(await getPref('hexstyleLwr'))) {
|
|
363
|
+
xstyle = 'X';
|
|
364
|
+
} */
|
|
365
|
+
const hexLettersUpper = await getPref('hexLettersUpper');
|
|
366
|
+
return out.replaceAll(decim, function (match, match1) {
|
|
367
|
+
let hexletters = Number(match1).toString(16);
|
|
368
|
+
if (hexLettersUpper) {
|
|
369
|
+
hexletters = hexletters.toUpperCase();
|
|
370
|
+
}
|
|
371
|
+
return '&#' + xstyle + hexletters + ';';
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* @param {string} toconvert
|
|
377
|
+
* @returns {string}
|
|
378
|
+
*/
|
|
379
|
+
cssescape2unicodeval (toconvert) {
|
|
380
|
+
// See:
|
|
381
|
+
// https://www.w3.org/TR/CSS21/syndata.html#characters
|
|
382
|
+
// https://www.w3.org/TR/CSS21/grammar.html
|
|
383
|
+
let unicode = '';
|
|
384
|
+
for (let i = 0; i < toconvert.length; i++) {
|
|
385
|
+
const s = toconvert[i];
|
|
386
|
+
if (s !== '\\') {
|
|
387
|
+
unicode += s;
|
|
388
|
+
continue;
|
|
389
|
+
}
|
|
390
|
+
const next = toconvert[i + 1];
|
|
391
|
+
switch (next) {
|
|
392
|
+
/*
|
|
393
|
+
case '\r':
|
|
394
|
+
if (toconvert[i + 2] === '\n') {
|
|
395
|
+
unicode += s + next + toconvert[i + 2];
|
|
396
|
+
i++; // Skip the extra newline character here
|
|
397
|
+
break;
|
|
398
|
+
}
|
|
399
|
+
// Fallthrough
|
|
400
|
+
*/
|
|
401
|
+
case '\n':
|
|
402
|
+
// Fallthrough
|
|
403
|
+
case '\f':
|
|
404
|
+
// Copy as is:
|
|
405
|
+
unicode += s + next;
|
|
406
|
+
break;
|
|
407
|
+
default: {
|
|
408
|
+
const hexEsc = toconvert.slice(i + 1).match(/^([A-Fa-f\d]{1,5})(?:([A-Fa-f\d])|(\r\n|[ \t\r\n\f])?)/u); // 1-5 hex and WS, or 6 hex
|
|
409
|
+
if (hexEsc) {
|
|
410
|
+
i += hexEsc[0].length - 1; // We want to skip the whole structure
|
|
411
|
+
const hex = hexEsc[1] + (hexEsc[2] || ''); // [2] only if is 6-digit
|
|
412
|
+
const dec = Number.parseInt(hex, 16);
|
|
413
|
+
|
|
414
|
+
// \u000 is disallowed in CSS 2.1 (behavior undefined) and above
|
|
415
|
+
// 0x10FFFF is beyond valid Unicode; fix: disallow non-characters
|
|
416
|
+
// too?
|
|
417
|
+
if (dec > 0x10FFFF || dec === 0) {
|
|
418
|
+
// Replacement character since not valid Unicode
|
|
419
|
+
unicode += '\uFFFD';
|
|
420
|
+
break;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
const hexStr = String.fromCodePoint(dec);
|
|
424
|
+
|
|
425
|
+
// Too low ASCII to be converted (not a letter, digit,
|
|
426
|
+
// underscore, or hyphen)
|
|
427
|
+
// eslint-disable-next-line unicorn/prefer-ternary -- Structure
|
|
428
|
+
if (dec < 0xA1 && (/[^\w-]/u).test(hexStr)) {
|
|
429
|
+
// Don't convert since won't be valid if unescaped
|
|
430
|
+
// Although https://www.w3.org/TR/CSS21/grammar.html#scanner
|
|
431
|
+
// (under "nonascii" which is a possible (indirect) component
|
|
432
|
+
// of identifiers) seems to permit any non-ASCII equal to or
|
|
433
|
+
// above 0x80 (decimal 128), per https://www.w3.org/TR/CSS21/syndata.html#characters
|
|
434
|
+
// only non-escaped characters above 0xA1 are permitted
|
|
435
|
+
// (limitation of Flex scanner based in Latin?); testing in
|
|
436
|
+
// Firefox also shows values lower than 0xA1 in CSS do not work
|
|
437
|
+
// there unless escaped
|
|
438
|
+
unicode += s + hexEsc[0];
|
|
439
|
+
// If begins with a digit or hyphen and digit, might not be valid
|
|
440
|
+
// if unescaped (if at beginning of identifier) so don't
|
|
441
|
+
// convert (if followed by an escaped number, there is no concern
|
|
442
|
+
// it will be avoided here, since the escaped number will remain
|
|
443
|
+
// escaped on the next iteration (by this same condition)
|
|
444
|
+
// Reenable if know what this is supposed to be!
|
|
445
|
+
/* } else if ((/^-?\d/u).test(hexStr + toconvert[i + 2])) {
|
|
446
|
+
unicode += s + hexEsc[0]; */
|
|
447
|
+
} else {
|
|
448
|
+
unicode += hexStr;
|
|
449
|
+
}
|
|
450
|
+
} else {
|
|
451
|
+
// [^\r\n\f0-9a-f] // May be escaping something that needs
|
|
452
|
+
// to be escaped for CSS grammar, so keep
|
|
453
|
+
unicode += s + next;
|
|
454
|
+
}
|
|
455
|
+
break;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
i++;
|
|
459
|
+
}
|
|
460
|
+
return unicode;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/**
|
|
464
|
+
* @param {string} toconvert
|
|
465
|
+
* @param {UnicodeEscapeMode} mode
|
|
466
|
+
* @returns {string}
|
|
467
|
+
*/
|
|
468
|
+
jsescape2unicodeval (toconvert, mode) {
|
|
469
|
+
let unicode = '', hexChrs;
|
|
470
|
+
for (let i = 0; i < toconvert.length; i++) {
|
|
471
|
+
const s = toconvert[i];
|
|
472
|
+
if (s !== '\\') {
|
|
473
|
+
unicode += s;
|
|
474
|
+
} else {
|
|
475
|
+
const next = toconvert[i + 1];
|
|
476
|
+
if (mode === 'php') {
|
|
477
|
+
switch (next) {
|
|
478
|
+
case '\\': // Just add one backslash
|
|
479
|
+
unicode += s;
|
|
480
|
+
break;
|
|
481
|
+
case 'u':
|
|
482
|
+
hexChrs = (/^[a-fA-F\d]{6}|[a-fA-F\d]{4}/u).exec(toconvert.slice(i + 2));
|
|
483
|
+
if (hexChrs) {
|
|
484
|
+
unicode += String.fromCodePoint(
|
|
485
|
+
Number.parseInt(hexChrs[0], 16)
|
|
486
|
+
);
|
|
487
|
+
i += hexChrs[0].length; // 4 or 6
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
// Fallthrough
|
|
491
|
+
default:
|
|
492
|
+
unicode += s + next;
|
|
493
|
+
break;
|
|
494
|
+
}
|
|
495
|
+
} else {
|
|
496
|
+
switch (next) {
|
|
497
|
+
case '\\': // Just add one backslash
|
|
498
|
+
unicode += s;
|
|
499
|
+
break;
|
|
500
|
+
case 'r':
|
|
501
|
+
unicode += '\u000D';
|
|
502
|
+
break;
|
|
503
|
+
case 'n':
|
|
504
|
+
unicode += '\n';
|
|
505
|
+
break;
|
|
506
|
+
case 't':
|
|
507
|
+
unicode += '\t';
|
|
508
|
+
break;
|
|
509
|
+
case 'f':
|
|
510
|
+
unicode += '\f';
|
|
511
|
+
break;
|
|
512
|
+
case 'v':
|
|
513
|
+
unicode += '\v';
|
|
514
|
+
break;
|
|
515
|
+
case 'b':
|
|
516
|
+
unicode += '\b';
|
|
517
|
+
break;
|
|
518
|
+
case 'u':
|
|
519
|
+
hexChrs = (/^[a-fA-F\d]{4}/u).exec(toconvert.slice(i + 2));
|
|
520
|
+
if (hexChrs) {
|
|
521
|
+
unicode += String.fromCharCode(Number.parseInt(hexChrs[0], 16));
|
|
522
|
+
i += hexChrs[0].length; // 4
|
|
523
|
+
break;
|
|
524
|
+
}
|
|
525
|
+
// Fallthrough
|
|
526
|
+
default: // Unrecognized escape, so just add both characters
|
|
527
|
+
unicode += s + next;
|
|
528
|
+
break;
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
i++;
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
return unicode;
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
/**
|
|
538
|
+
* @param {string} toconvert
|
|
539
|
+
* @returns {string}
|
|
540
|
+
*/
|
|
541
|
+
async unicode2jsescapeval (toconvert) {
|
|
542
|
+
return await this.unicode2charrefHexval(toconvert, true, 'javascript');
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
/**
|
|
546
|
+
* @param {string} toconvert
|
|
547
|
+
* @returns {string}
|
|
548
|
+
*/
|
|
549
|
+
async unicodeTo6DigitVal (toconvert) {
|
|
550
|
+
return await this.unicode2charrefHexval(toconvert, false, 'php');
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* @param {string} toconvert
|
|
555
|
+
* @returns {string}
|
|
556
|
+
*/
|
|
557
|
+
async unicode2cssescapeval (toconvert) {
|
|
558
|
+
return await this.unicode2charrefHexval(toconvert, false, 'css');
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
/**
|
|
562
|
+
* @param {string} toconvert
|
|
563
|
+
* @returns {string}
|
|
564
|
+
*/
|
|
565
|
+
sixDigit2UnicodeVal (toconvert) {
|
|
566
|
+
return this.jsescape2unicodeval(toconvert, 'php');
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
// Todo: Move these to their own database-driven file?
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* @param {string} toconvert
|
|
573
|
+
* @returns {string}
|
|
574
|
+
*/
|
|
575
|
+
async unicode2CharDescVal (toconvert) {
|
|
576
|
+
const asciiLt128 = await getPref('asciiLt128');
|
|
577
|
+
return (await Promise.all([...toconvert].map(async (ch) => {
|
|
578
|
+
const codePoint = ch.codePointAt();
|
|
579
|
+
if (codePoint >= 128 || asciiLt128) {
|
|
580
|
+
const charDesc = await this.getCharDescForCodePoint(codePoint);
|
|
581
|
+
if (charDesc) { // Skip if no description in database
|
|
582
|
+
return String.raw`\C{` + charDesc + '}';
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
return ch;
|
|
586
|
+
}))).join('');
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
/**
|
|
590
|
+
* @param {string} toconvert
|
|
591
|
+
* @returns {Promise<string>}
|
|
592
|
+
*/
|
|
593
|
+
async charDesc2UnicodeVal (toconvert) {
|
|
594
|
+
const promises = [];
|
|
595
|
+
toconvert.replaceAll(/\\C\{([^}]*)\}/gu, (n, n1) => {
|
|
596
|
+
promises.push(this.lookupUnicodeValueByCharName(n1));
|
|
597
|
+
});
|
|
598
|
+
|
|
599
|
+
const unicodeVals = await Promise.all(promises);
|
|
600
|
+
|
|
601
|
+
let i = -1;
|
|
602
|
+
return toconvert.replaceAll(/\\C\{([^}]*)\}/gu, () => {
|
|
603
|
+
++i;
|
|
604
|
+
return unicodeVals[i]
|
|
605
|
+
? String.fromCodePoint(unicodeVals[i])
|
|
606
|
+
: '\uFFFD'; // Replacement character if not found?
|
|
607
|
+
});
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* Obtain a Unicode character description for a given decimal-expressed
|
|
612
|
+
* code point.
|
|
613
|
+
* @param {Integer} dec The code point of the description to obtain
|
|
614
|
+
* @returns {string} The Unicode character description
|
|
615
|
+
*/
|
|
616
|
+
async getCharDescForCodePoint (dec) {
|
|
617
|
+
// Todo: This should support CJK and those which are only marked by
|
|
618
|
+
// ranges (e.g., surrogates, though for these, see calling code)
|
|
619
|
+
try {
|
|
620
|
+
if (dec >= 0xAC00 && dec <= 0xD7A3) {
|
|
621
|
+
return getHangulName(dec);
|
|
622
|
+
}
|
|
623
|
+
|
|
624
|
+
const hexStr = dec.toString(16).toUpperCase().padStart(4, '0');
|
|
625
|
+
|
|
626
|
+
await charrefunicodeDb.connect();
|
|
627
|
+
const {
|
|
628
|
+
name, unicode1Name
|
|
629
|
+
} = await charrefunicodeDb.getUnicodeFields(hexStr);
|
|
630
|
+
|
|
631
|
+
// Todo: Unihan
|
|
632
|
+
/*
|
|
633
|
+
if (!name) {
|
|
634
|
+
}
|
|
635
|
+
*/
|
|
636
|
+
|
|
637
|
+
if (unicode1Name && name.includes('<')) {
|
|
638
|
+
return `${unicode1Name} (${name})`;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
return name;
|
|
642
|
+
/* istanbul ignore next -- Debugging */
|
|
643
|
+
} catch (e) {
|
|
644
|
+
/* istanbul ignore next -- Debugging */
|
|
645
|
+
alert(e);
|
|
646
|
+
}
|
|
647
|
+
/* istanbul ignore next -- Debugging */
|
|
648
|
+
return undefined;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
/**
|
|
652
|
+
* Search for a Unicode character value matching a given description.
|
|
653
|
+
* @param {string} value
|
|
654
|
+
* @returns {Integer}
|
|
655
|
+
*/
|
|
656
|
+
async lookupUnicodeValueByCharName (value) {
|
|
657
|
+
// todo: Character names for Unihan
|
|
658
|
+
const forceUnicode = true;
|
|
659
|
+
const table = forceUnicode
|
|
660
|
+
? 'UnicodeData'
|
|
661
|
+
/* istanbul ignore next -- Known todo */
|
|
662
|
+
: 'Unihan';
|
|
663
|
+
const id = forceUnicode
|
|
664
|
+
? 'searchName'
|
|
665
|
+
/* istanbul ignore next -- Known todo */
|
|
666
|
+
: 'searchkDefinition';
|
|
667
|
+
await this.searchUnicode(
|
|
668
|
+
{id, value}, table, 'noChart=true', 'strict=true'
|
|
669
|
+
);
|
|
670
|
+
if (!this.descripts[0] && value.length <= 7) {
|
|
671
|
+
// Try Hangul (if possible size for Hangul)
|
|
672
|
+
// Fix: Is Hangul allowed in PHP 6 Unicode escape names?
|
|
673
|
+
const ret = getHangulFromName(value);
|
|
674
|
+
return ret ? ret.charCodeAt(0) : false;
|
|
675
|
+
}
|
|
676
|
+
return this.descripts[0];
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
// Used for conversions, so included here (also used externally)
|
|
680
|
+
/**
|
|
681
|
+
* @param {{id: string, value: string}} obj E.g., an input element
|
|
682
|
+
* @param {string} table
|
|
683
|
+
* @param {boolean} nochart
|
|
684
|
+
* @param {boolean} strict
|
|
685
|
+
* @returns {Promise<void>}
|
|
686
|
+
*/
|
|
687
|
+
async searchUnicode (obj, table, nochart, strict) { // Fix: allow Jamo!
|
|
688
|
+
if (!table) {
|
|
689
|
+
table = 'UnicodeData';
|
|
690
|
+
}
|
|
691
|
+
// const table = 'Unihan'; // fix: determine by pull-down
|
|
692
|
+
const nameDescVal = obj.value;
|
|
693
|
+
|
|
694
|
+
/* istanbul ignore next -- Just a guard */
|
|
695
|
+
if (
|
|
696
|
+
// Don't query the other databases here
|
|
697
|
+
(obj.id.startsWith('searchk') && table === 'UnicodeData') ||
|
|
698
|
+
((/^search[^k]/u).test(obj.id) && table === 'Unihan')
|
|
699
|
+
) {
|
|
700
|
+
return;
|
|
701
|
+
}
|
|
702
|
+
const nameDesc = obj.id.replace(/^search/u, '');
|
|
703
|
+
|
|
704
|
+
// const nameDesc = (table === 'Unihan') ? 'kDefinition'
|
|
705
|
+
// : 'Name'; // Fix: let Unihan search Mandarin, etc.
|
|
706
|
+
|
|
707
|
+
const conn = table === 'Unihan'
|
|
708
|
+
? unicodecharref.unihanDatabase
|
|
709
|
+
: charrefunicodeDb;
|
|
710
|
+
|
|
711
|
+
if (table === 'Unihan' && !nochart && !unicodecharref.unihanDb_exists) {
|
|
712
|
+
alert(this._('need_download_unihan'));
|
|
713
|
+
return;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
await conn.connect();
|
|
717
|
+
this.descripts = [];
|
|
718
|
+
|
|
719
|
+
try {
|
|
720
|
+
/*
|
|
721
|
+
if (nameDesc === 'General_Category' && nameDescVal === 'Cn') {
|
|
722
|
+
try {
|
|
723
|
+
const chars = await conn.getAll();
|
|
724
|
+
let j = 0;
|
|
725
|
+
for (let i = 0; i < 0x10FFFE; i++) {
|
|
726
|
+
let {name, codePoint} = chars[j++];
|
|
727
|
+
const range = name.endsWith('First>');
|
|
728
|
+
if (range) {
|
|
729
|
+
({name, codePoint} = chars[j++]);
|
|
730
|
+
const endRange = name.endsWith('Last>');
|
|
731
|
+
if (endRange) {
|
|
732
|
+
i = Number.parseInt(codePoint, 16);
|
|
733
|
+
continue;
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
let hex = Number.parseInt(codePoint, 16);
|
|
737
|
+
for (let endHex = hex; i < endHex; i++, hex++) {
|
|
738
|
+
this.descripts.push(i);
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
} catch (e) {
|
|
742
|
+
alert(e);
|
|
743
|
+
}
|
|
744
|
+
} else { */
|
|
745
|
+
const field = nameDesc;
|
|
746
|
+
const camelizedField = camelCase(nameDesc);
|
|
747
|
+
|
|
748
|
+
// Todo: Add indexes for each instead and then query with
|
|
749
|
+
// `nameDescVal`, at least for `strict`
|
|
750
|
+
const chars = await conn.getAll();
|
|
751
|
+
|
|
752
|
+
const filteredChars = strict
|
|
753
|
+
? chars.filter((chr) => {
|
|
754
|
+
const cell = table === 'Unihan'
|
|
755
|
+
/* istanbul ignore next -- Not yet using strict checking */
|
|
756
|
+
? chr.columns[
|
|
757
|
+
unicodecharref.Unihan.indexOf(field)
|
|
758
|
+
]
|
|
759
|
+
: chr[camelizedField];
|
|
760
|
+
return cell.toLowerCase() === nameDescVal.toLowerCase();
|
|
761
|
+
})
|
|
762
|
+
: chars.filter((chr) => {
|
|
763
|
+
const cell = table === 'Unihan'
|
|
764
|
+
? chr.columns[
|
|
765
|
+
unicodecharref.Unihan.indexOf(field)
|
|
766
|
+
]
|
|
767
|
+
: chr[camelizedField];
|
|
768
|
+
return cell.toLowerCase().includes(
|
|
769
|
+
nameDescVal.toLowerCase()
|
|
770
|
+
);
|
|
771
|
+
});
|
|
772
|
+
|
|
773
|
+
filteredChars.forEach((filteredChar) => {
|
|
774
|
+
const {codePoint} = filteredChar;
|
|
775
|
+
const hex = Number.parseInt(codePoint, 16);
|
|
776
|
+
if (table === 'UnicodeData' &&
|
|
777
|
+
(hex >= 0xF900 && hex < 0xFB00)
|
|
778
|
+
) { // Don't search for compatibility if searching Unicode
|
|
779
|
+
return;
|
|
780
|
+
}
|
|
781
|
+
// Fix: inefficient, but fits more easily into current pattern
|
|
782
|
+
this.descripts.push(hex);
|
|
783
|
+
});
|
|
784
|
+
/* istanbul ignore next -- Debugging */
|
|
785
|
+
} catch (e) {
|
|
786
|
+
/* istanbul ignore next -- Debugging */
|
|
787
|
+
alert(e);
|
|
788
|
+
} finally {
|
|
789
|
+
// conn.close();
|
|
790
|
+
}
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
};
|