| 'use strict'; |
| // based on https://github.com/bestiejs/punycode.js/blob/master/punycode.js |
| var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1 |
| var base = 36; |
| var tMin = 1; |
| var tMax = 26; |
| var skew = 38; |
| var damp = 700; |
| var initialBias = 72; |
| var initialN = 128; // 0x80 |
| var delimiter = '-'; // '\x2D' |
| var regexNonASCII = /[^\0-\u007E]/; // non-ASCII chars |
| var regexSeparators = /[.\u3002\uFF0E\uFF61]/g; // RFC 3490 separators |
| var OVERFLOW_ERROR = 'Overflow: input needs wider integers to process'; |
| var baseMinusTMin = base - tMin; |
| var floor = Math.floor; |
| var stringFromCharCode = String.fromCharCode; |
| |
| /** |
| * Creates an array containing the numeric code points of each Unicode |
| * character in the string. While JavaScript uses UCS-2 internally, |
| * this function will convert a pair of surrogate halves (each of which |
| * UCS-2 exposes as separate characters) into a single code point, |
| * matching UTF-16. |
| */ |
| var ucs2decode = function (string) { |
| var output = []; |
| var counter = 0; |
| var length = string.length; |
| while (counter < length) { |
| var value = string.charCodeAt(counter++); |
| if (value >= 0xD800 && value <= 0xDBFF && counter < length) { |
| // It's a high surrogate, and there is a next character. |
| var extra = string.charCodeAt(counter++); |
| if ((extra & 0xFC00) == 0xDC00) { // Low surrogate. |
| output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000); |
| } else { |
| // It's an unmatched surrogate; only append this code unit, in case the |
| // next code unit is the high surrogate of a surrogate pair. |
| output.push(value); |
| counter--; |
| } |
| } else { |
| output.push(value); |
| } |
| } |
| return output; |
| }; |
| |
| /** |
| * Converts a digit/integer into a basic code point. |
| */ |
| var digitToBasic = function (digit) { |
| // 0..25 map to ASCII a..z or A..Z |
| // 26..35 map to ASCII 0..9 |
| return digit + 22 + 75 * (digit < 26); |
| }; |
| |
| /** |
| * Bias adaptation function as per section 3.4 of RFC 3492. |
| * https://tools.ietf.org/html/rfc3492#section-3.4 |
| */ |
| var adapt = function (delta, numPoints, firstTime) { |
| var k = 0; |
| delta = firstTime ? floor(delta / damp) : delta >> 1; |
| delta += floor(delta / numPoints); |
| for (; delta > baseMinusTMin * tMax >> 1; k += base) { |
| delta = floor(delta / baseMinusTMin); |
| } |
| return floor(k + (baseMinusTMin + 1) * delta / (delta + skew)); |
| }; |
| |
| /** |
| * Converts a string of Unicode symbols (e.g. a domain name label) to a |
| * Punycode string of ASCII-only symbols. |
| */ |
| // eslint-disable-next-line max-statements |
| var encode = function (input) { |
| var output = []; |
| |
| // Convert the input in UCS-2 to an array of Unicode code points. |
| input = ucs2decode(input); |
| |
| // Cache the length. |
| var inputLength = input.length; |
| |
| // Initialize the state. |
| var n = initialN; |
| var delta = 0; |
| var bias = initialBias; |
| var i, currentValue; |
| |
| // Handle the basic code points. |
| for (i = 0; i < input.length; i++) { |
| currentValue = input[i]; |
| if (currentValue < 0x80) { |
| output.push(stringFromCharCode(currentValue)); |
| } |
| } |
| |
| var basicLength = output.length; // number of basic code points. |
| var handledCPCount = basicLength; // number of code points that have been handled; |
| |
| // Finish the basic string with a delimiter unless it's empty. |
| if (basicLength) { |
| output.push(delimiter); |
| } |
| |
| // Main encoding loop: |
| while (handledCPCount < inputLength) { |
| // All non-basic code points < n have been handled already. Find the next larger one: |
| var m = maxInt; |
| for (i = 0; i < input.length; i++) { |
| currentValue = input[i]; |
| if (currentValue >= n && currentValue < m) { |
| m = currentValue; |
| } |
| } |
| |
| // Increase `delta` enough to advance the decoder's <n,i> state to <m,0>, but guard against overflow. |
| var handledCPCountPlusOne = handledCPCount + 1; |
| if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) { |
| throw RangeError(OVERFLOW_ERROR); |
| } |
| |
| delta += (m - n) * handledCPCountPlusOne; |
| n = m; |
| |
| for (i = 0; i < input.length; i++) { |
| currentValue = input[i]; |
| if (currentValue < n && ++delta > maxInt) { |
| throw RangeError(OVERFLOW_ERROR); |
| } |
| if (currentValue == n) { |
| // Represent delta as a generalized variable-length integer. |
| var q = delta; |
| for (var k = base; /* no condition */; k += base) { |
| var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias); |
| if (q < t) break; |
| var qMinusT = q - t; |
| var baseMinusT = base - t; |
| output.push(stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT))); |
| q = floor(qMinusT / baseMinusT); |
| } |
| |
| output.push(stringFromCharCode(digitToBasic(q))); |
| bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength); |
| delta = 0; |
| ++handledCPCount; |
| } |
| } |
| |
| ++delta; |
| ++n; |
| } |
| return output.join(''); |
| }; |
| |
| module.exports = function (input) { |
| var encoded = []; |
| var labels = input.toLowerCase().replace(regexSeparators, '\u002E').split('.'); |
| var i, label; |
| for (i = 0; i < labels.length; i++) { |
| label = labels[i]; |
| encoded.push(regexNonASCII.test(label) ? 'xn--' + encode(label) : label); |
| } |
| return encoded.join('.'); |
| }; |