punycode

This tag converts Punycode

Created by: hackvertor
Installed 1 times

Category: Convert

Created on: Wednesday, November 27, 2024 at 9:00:11 AM

Updated on: Tuesday, December 3, 2024 at 11:12:53 PM

This is a built in tag
Tag arguments
[]
Code
class punycode {
  encode(input) {
    return this.toPunycode(input);
  }

  decode(input) {
    return this.fromPunycode(input);
  }

  matches(input) {
    const punycodeRegex = /^xn--[a-z0-9-]+$/i;
    const result = punycodeRegex.exec(input);
    return result ? result : null;
  }

  toPunycode(input) {
    const delimiter = "-";
    const base = 36;
    const tMin = 1;
    const tMax = 26;
    const skew = 38;
    const damp = 700;
    const initialBias = 72;
    const initialN = 128;
    const prefix = "xn--";

    let n = initialN;
    let delta = 0;
    let bias = initialBias;
    let output = "";

    const basicChars = Array.from(input).filter(
      (char) => char.charCodeAt(0) < 128,
    );
    output += basicChars.join("");
    const handledCPCount = basicChars.length;

    if (handledCPCount) {
      output += delimiter;
    }

    let inputCodePoints = Array.from(input).map((char) => char.charCodeAt(0));
    let handledCPIndex = handledCPCount;

    while (handledCPIndex < inputCodePoints.length) {
      let minCodePoint = Math.min(...inputCodePoints.filter((cp) => cp >= n));
      delta += (minCodePoint - n) * (handledCPIndex + 1);
      n = minCodePoint;

      for (let cp of inputCodePoints) {
        if (cp < n) {
          delta++;
        } else if (cp === n) {
          let q = delta;
          for (let k = base; ; k += base) {
            let t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
            if (q < t) {
              break;
            }
            output += String.fromCharCode(
              this.digitToBasic(t + ((q - t) % (base - t)), false),
            );
            q = Math.floor((q - t) / (base - t));
          }
          output += String.fromCharCode(this.digitToBasic(q, false));
          bias = this.adapt(
            delta,
            handledCPIndex + 1,
            handledCPIndex === handledCPCount,
          );
          delta = 0;
          handledCPIndex++;
        }
      }
      delta++;
      n++;
    }
    return prefix + output;
  }

  fromPunycode(input) {
    const base = 36;
    const tMin = 1;
    const tMax = 26;
    const initialN = 128;
    const initialBias = 72;
    const delimiter = "-";

    if (!input.startsWith("xn--")) {
      throw new Error("Not a valid Punycode string");
    }
    input = input.slice(4);

    let n = initialN;
    let i = 0;
    let bias = initialBias;
    let output = [];

    const delimiterIndex = input.lastIndexOf(delimiter);
    if (delimiterIndex > -1) {
      output = Array.from(input.slice(0, delimiterIndex)).map((char) =>
        char.charCodeAt(0),
      );
      input = input.slice(delimiterIndex + 1);
    }

    let inputIndex = 0;
    while (inputIndex < input.length) {
      let oldi = i;
      let w = 1;
      for (let k = base; ; k += base) {
        const digit = this.basicToDigit(input.charCodeAt(inputIndex++));
        i += digit * w;
        const t = k <= bias ? tMin : k >= bias + tMax ? tMax : k - bias;
        if (digit < t) {
          break;
        }
        w *= base - t;
      }
      bias = this.adapt(i - oldi, output.length + 1, oldi === 0);
      n += Math.floor(i / (output.length + 1));
      i %= output.length + 1;
      output.splice(i++, 0, n);
    }

    return String.fromCodePoint(...output);
  }

  basicToDigit(codePoint) {
    if (codePoint - 48 < 10) {
      return codePoint - 22;
    }
    if (codePoint - 65 < 26) {
      return codePoint - 65;
    }
    if (codePoint - 97 < 26) {
      return codePoint - 97;
    }
    return base;
  }

  digitToBasic(digit, flag) {
    return digit + 22 + 75 * (digit < 26) - ((flag ? 1 : 0) << 5);
  }

  adapt(delta, numPoints, firstTime) {
    const base = 36;
    const tMin = 1;
    const tMax = 26;
    const skew = 38;
    const damp = 700;
    delta = firstTime ? Math.floor(delta / damp) : delta >> 1;
    delta += Math.floor(delta / numPoints);
    let k = 0;
    while (delta > ((base - tMin) * tMax) >> 1) {
      delta = Math.floor(delta / (base - tMin));
      k += base;
    }
    return k + Math.floor(((base - tMin + 1) * delta) / (delta + skew));
  }
}