186 lines
5.3 KiB
JavaScript
186 lines
5.3 KiB
JavaScript
/* eslint-disable no-nested-ternary */
|
|
/* eslint-disable no-bitwise */
|
|
/* eslint-disable @typescript-eslint/naming-convention */
|
|
|
|
const utf16 = {
|
|
// The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
|
|
decode: (input) => {
|
|
const output = [];
|
|
let i = 0;
|
|
const len = input.length;
|
|
let value;
|
|
let extra;
|
|
|
|
while (i < len) {
|
|
value = input.charCodeAt(i++);
|
|
if ((value & 0xf800) === 0xd800) {
|
|
extra = input.charCodeAt(i++);
|
|
if ((value & 0xfc00) !== 0xd800 || (extra & 0xfc00) !== 0xdc00) {
|
|
throw new RangeError('UTF-16(decode): Illegal UTF-16 sequence');
|
|
}
|
|
value = ((value & 0x3ff) << 10) + (extra & 0x3ff) + 0x10000;
|
|
}
|
|
output.push(value);
|
|
}
|
|
return output;
|
|
},
|
|
encode: (input) => {
|
|
const output = [];
|
|
let i = 0;
|
|
const len = input.length;
|
|
let value;
|
|
|
|
while (i < len) {
|
|
value = input[i++];
|
|
if ((value & 0xf800) === 0xd800) {
|
|
throw new RangeError('UTF-16(encode): Illegal UTF-16 value');
|
|
}
|
|
if (value > 0xffff) {
|
|
value -= 0x10000;
|
|
output.push(String.fromCharCode(((value >>> 10) & 0x3ff) | 0xd800));
|
|
value = 0xdc00 | (value & 0x3ff);
|
|
}
|
|
output.push(String.fromCharCode(value));
|
|
}
|
|
return output.join('');
|
|
},
|
|
};
|
|
|
|
// Default parameters
|
|
const initial_n = 0x80;
|
|
const initial_bias = 72;
|
|
const delimiter = '\x2D';
|
|
const base = 36;
|
|
const damp = 700;
|
|
const tmin = 1;
|
|
const tmax = 26;
|
|
const skew = 38;
|
|
const maxint = 0x7fffffff;
|
|
|
|
// decode_digit(cp) returns the numeric value of a basic code
|
|
// point (for use in representing integers) in the range 0 to
|
|
// base-1, or base if cp is does not represent a value.
|
|
|
|
function decode_digit(cp) {
|
|
return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
|
|
}
|
|
|
|
// ** Bias adaptation function **
|
|
function adapt(delta, numpoints, firsttime) {
|
|
let k;
|
|
delta = firsttime ? Math.floor(delta / damp) : delta >> 1;
|
|
delta += Math.floor(delta / numpoints);
|
|
|
|
for (k = 0; delta > ((base - tmin) * tmax) >> 1; k += base) {
|
|
delta = Math.floor(delta / (base - tmin));
|
|
}
|
|
return Math.floor(k + ((base - tmin + 1) * delta) / (delta + skew));
|
|
}
|
|
|
|
// Main decode
|
|
function decode(input, preserveCase) {
|
|
// Dont use utf16
|
|
const output = [];
|
|
const case_flags = [];
|
|
const input_length = input.length;
|
|
|
|
let n; let out; let i; let bias; let basic; let j; let ic; let oldi; let w; let k; let digit; let t; let len;
|
|
|
|
// Initialize the state:
|
|
|
|
n = initial_n;
|
|
i = 0;
|
|
bias = initial_bias;
|
|
|
|
// Handle the basic code points: Let basic be the number of input code
|
|
// points before the last delimiter, or 0 if there is none, then
|
|
// copy the first basic code points to the output.
|
|
|
|
basic = input.lastIndexOf(delimiter);
|
|
if (basic < 0) basic = 0;
|
|
|
|
for (j = 0; j < basic; ++j) {
|
|
if (preserveCase) case_flags[output.length] = input.charCodeAt(j) - 65 < 26;
|
|
if (input.charCodeAt(j) >= 0x80) {
|
|
throw new RangeError('Illegal input >= 0x80');
|
|
}
|
|
output.push(input.charCodeAt(j));
|
|
}
|
|
|
|
// Main decoding loop: Start just after the last delimiter if any
|
|
// basic code points were copied; start at the beginning otherwise.
|
|
|
|
for (ic = basic > 0 ? basic + 1 : 0; ic < input_length;) {
|
|
// ic is the index of the next character to be consumed,
|
|
|
|
// Decode a generalized variable-length integer into delta,
|
|
// which gets added to i. The overflow checking is easier
|
|
// if we increase i as we go, then subtract off its starting
|
|
// value at the end to obtain delta.
|
|
for (oldi = i, w = 1, k = base; ; k += base) {
|
|
if (ic >= input_length) {
|
|
throw RangeError('punycode_bad_input(1)');
|
|
}
|
|
digit = decode_digit(input.charCodeAt(ic++));
|
|
|
|
if (digit >= base) {
|
|
throw RangeError('punycode_bad_input(2)');
|
|
}
|
|
if (digit > Math.floor((maxint - i) / w)) {
|
|
throw RangeError('punycode_overflow(1)');
|
|
}
|
|
i += digit * w;
|
|
t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
|
|
if (digit < t) {
|
|
break;
|
|
}
|
|
if (w > Math.floor(maxint / (base - t))) {
|
|
throw RangeError('punycode_overflow(2)');
|
|
}
|
|
w *= base - t;
|
|
}
|
|
|
|
out = output.length + 1;
|
|
bias = adapt(i - oldi, out, oldi === 0);
|
|
|
|
// i was supposed to wrap around from out to 0,
|
|
// incrementing n each time, so we'll fix that now:
|
|
if (Math.floor(i / out) > maxint - n) {
|
|
throw RangeError('punycode_overflow(3)');
|
|
}
|
|
n += Math.floor(i / out);
|
|
i %= out;
|
|
|
|
// Insert n at position i of the output:
|
|
// Case of last character determines uppercase flag:
|
|
if (preserveCase) {
|
|
case_flags.splice(i, 0, input.charCodeAt(ic - 1) - 65 < 26);
|
|
}
|
|
|
|
output.splice(i, 0, n);
|
|
i++;
|
|
}
|
|
if (preserveCase) {
|
|
for (i = 0, len = output.length; i < len; i++) {
|
|
if (case_flags[i]) {
|
|
output[i] = String.fromCharCode(output[i])
|
|
.toUpperCase()
|
|
.charCodeAt(0);
|
|
}
|
|
}
|
|
}
|
|
return utf16.encode(output);
|
|
}
|
|
|
|
function toUnicode(domain) {
|
|
const domain_array = domain.split('.');
|
|
const out = [];
|
|
for (let i = 0; i < domain_array.length; ++i) {
|
|
const s = domain_array[i];
|
|
out.push(s.match(/^xn--/) ? decode(s.slice(4)) : s);
|
|
}
|
|
return out.join('.');
|
|
}
|
|
|
|
export default toUnicode;
|