punycode.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. /** @license
  2. ========================================================================
  3. Javascript Punycode converter derived from example in RFC3492.
  4. This implementation is created by some@domain.name and released into public domain
  5. From RFC3492:
  6. Disclaimer and license
  7. Regarding this entire document or any portion of it (including the
  8. pseudocode and C code), the author makes no guarantees and is not
  9. responsible for any damage resulting from its use. The author grants
  10. irrevocable permission to anyone to use, modify, and distribute it in
  11. any way that does not diminish the rights of anyone else to use,
  12. modify, and distribute it, provided that redistributed derivative works do not contain misleading author or version information. Derivative works need not be licensed under similar terms.
  13. I put my work in this punycode and utf16 in the public domain.
  14. */
  15. "use strict";
  16. var punycode = new function Punycode() {
  17. // This object converts to and from puny-code used in IDN
  18. //
  19. // punycode.ToASCII(domain)
  20. //
  21. // Returns a puny coded representation of "domain".
  22. // It only converts the part of the domain name that
  23. // has non ASCII characters. I.e. it dosent matter if
  24. // you call it with a domain that already is in ASCII.
  25. //
  26. // punycode.ToUnicode(domain)
  27. //
  28. // Converts a puny-coded domain name to unicode.
  29. // It only converts the puny-coded parts of the domain name.
  30. // I.e. it dosent matter if you call it on a string
  31. // that already has been converted to unicode.
  32. //
  33. //
  34. this.utf16 = {
  35. // The utf16-class is necessary to convert from javascripts internal character representation to unicode and back.
  36. decode:function(input){
  37. var output = [], i=0, len=input.length,value,extra;
  38. while (i < len) {
  39. value = input.charCodeAt(i++);
  40. if ((value & 0xF800) === 0xD800) {
  41. extra = input.charCodeAt(i++);
  42. if ( ((value & 0xFC00) !== 0xD800) || ((extra & 0xFC00) !== 0xDC00) ) {
  43. throw new RangeError("UTF-16(decode): Illegal UTF-16 sequence");
  44. }
  45. value = ((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000;
  46. }
  47. output.push(value);
  48. }
  49. return output;
  50. },
  51. encode:function(input){
  52. var output = [], i=0, len=input.length,value;
  53. while (i < len) {
  54. value = input[i++];
  55. if ( (value & 0xF800) === 0xD800 ) {
  56. throw new RangeError("UTF-16(encode): Illegal UTF-16 value");
  57. }
  58. if (value > 0xFFFF) {
  59. value -= 0x10000;
  60. output.push(String.fromCharCode(((value >>>10) & 0x3FF) | 0xD800));
  61. value = 0xDC00 | (value & 0x3FF);
  62. }
  63. output.push(String.fromCharCode(value));
  64. }
  65. return output.join("");
  66. }
  67. }
  68. //Default parameters
  69. var initial_n = 0x80;
  70. var initial_bias = 72;
  71. var delimiter = "\x2D";
  72. var base = 36;
  73. var damp = 700;
  74. var tmin=1;
  75. var tmax=26;
  76. var skew=38;
  77. var maxint = 0x7FFFFFFF;
  78. // decode_digit(cp) returns the numeric value of a basic code
  79. // point (for use in representing integers) in the range 0 to
  80. // base-1, or base if cp is does not represent a value.
  81. function decode_digit(cp) {
  82. return cp - 48 < 10 ? cp - 22 : cp - 65 < 26 ? cp - 65 : cp - 97 < 26 ? cp - 97 : base;
  83. }
  84. // encode_digit(d,flag) returns the basic code point whose value
  85. // (when used for representing integers) is d, which needs to be in
  86. // the range 0 to base-1. The lowercase form is used unless flag is
  87. // nonzero, in which case the uppercase form is used. The behavior
  88. // is undefined if flag is nonzero and digit d has no uppercase form.
  89. function encode_digit(d, flag) {
  90. return d + 22 + 75 * (d < 26) - ((flag != 0) << 5);
  91. // 0..25 map to ASCII a..z or A..Z
  92. // 26..35 map to ASCII 0..9
  93. }
  94. //** Bias adaptation function **
  95. function adapt(delta, numpoints, firsttime ) {
  96. var k;
  97. delta = firsttime ? Math.floor(delta / damp) : (delta >> 1);
  98. delta += Math.floor(delta / numpoints);
  99. for (k = 0; delta > (((base - tmin) * tmax) >> 1); k += base) {
  100. delta = Math.floor(delta / ( base - tmin ));
  101. }
  102. return Math.floor(k + (base - tmin + 1) * delta / (delta + skew));
  103. }
  104. // encode_basic(bcp,flag) forces a basic code point to lowercase if flag is zero,
  105. // uppercase if flag is nonzero, and returns the resulting code point.
  106. // The code point is unchanged if it is caseless.
  107. // The behavior is undefined if bcp is not a basic code point.
  108. function encode_basic(bcp, flag) {
  109. bcp -= (bcp - 97 < 26) << 5;
  110. return bcp + ((!flag && (bcp - 65 < 26)) << 5);
  111. }
  112. // Main decode
  113. this.decode=function(input,preserveCase) {
  114. // Dont use utf16
  115. var output=[];
  116. var case_flags=[];
  117. var input_length = input.length;
  118. var n, out, i, bias, basic, j, ic, oldi, w, k, digit, t, len;
  119. // Initialize the state:
  120. n = initial_n;
  121. i = 0;
  122. bias = initial_bias;
  123. // Handle the basic code points: Let basic be the number of input code
  124. // points before the last delimiter, or 0 if there is none, then
  125. // copy the first basic code points to the output.
  126. basic = input.lastIndexOf(delimiter);
  127. if (basic < 0) basic = 0;
  128. for (j = 0; j < basic; ++j) {
  129. if(preserveCase) case_flags[output.length] = ( input.charCodeAt(j) -65 < 26);
  130. if ( input.charCodeAt(j) >= 0x80) {
  131. throw new RangeError("Illegal input >= 0x80");
  132. }
  133. output.push( input.charCodeAt(j) );
  134. }
  135. // Main decoding loop: Start just after the last delimiter if any
  136. // basic code points were copied; start at the beginning otherwise.
  137. for (ic = basic > 0 ? basic + 1 : 0; ic < input_length; ) {
  138. // ic is the index of the next character to be consumed,
  139. // Decode a generalized variable-length integer into delta,
  140. // which gets added to i. The overflow checking is easier
  141. // if we increase i as we go, then subtract off its starting
  142. // value at the end to obtain delta.
  143. for (oldi = i, w = 1, k = base; ; k += base) {
  144. if (ic >= input_length) {
  145. throw RangeError ("punycode_bad_input(1)");
  146. }
  147. digit = decode_digit(input.charCodeAt(ic++));
  148. if (digit >= base) {
  149. throw RangeError("punycode_bad_input(2)");
  150. }
  151. if (digit > Math.floor((maxint - i) / w)) {
  152. throw RangeError ("punycode_overflow(1)");
  153. }
  154. i += digit * w;
  155. t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  156. if (digit < t) { break; }
  157. if (w > Math.floor(maxint / (base - t))) {
  158. throw RangeError("punycode_overflow(2)");
  159. }
  160. w *= (base - t);
  161. }
  162. out = output.length + 1;
  163. bias = adapt(i - oldi, out, oldi === 0);
  164. // i was supposed to wrap around from out to 0,
  165. // incrementing n each time, so we'll fix that now:
  166. if ( Math.floor(i / out) > maxint - n) {
  167. throw RangeError("punycode_overflow(3)");
  168. }
  169. n += Math.floor( i / out ) ;
  170. i %= out;
  171. // Insert n at position i of the output:
  172. // Case of last character determines uppercase flag:
  173. if (preserveCase) { case_flags.splice(i, 0, input.charCodeAt(ic -1) -65 < 26);}
  174. output.splice(i, 0, n);
  175. i++;
  176. }
  177. if (preserveCase) {
  178. for (i = 0, len = output.length; i < len; i++) {
  179. if (case_flags[i]) {
  180. output[i] = (String.fromCharCode(output[i]).toUpperCase()).charCodeAt(0);
  181. }
  182. }
  183. }
  184. return this.utf16.encode(output);
  185. };
  186. //** Main encode function **
  187. this.encode = function (input,preserveCase) {
  188. //** Bias adaptation function **
  189. var n, delta, h, b, bias, j, m, q, k, t, ijv, case_flags;
  190. if (preserveCase) {
  191. // Preserve case, step1 of 2: Get a list of the unaltered string
  192. case_flags = this.utf16.decode(input);
  193. }
  194. // Converts the input in UTF-16 to Unicode
  195. input = this.utf16.decode(input.toLowerCase());
  196. var input_length = input.length; // Cache the length
  197. if (preserveCase) {
  198. // Preserve case, step2 of 2: Modify the list to true/false
  199. for (j=0; j < input_length; j++) {
  200. case_flags[j] = input[j] != case_flags[j];
  201. }
  202. }
  203. var output=[];
  204. // Initialize the state:
  205. n = initial_n;
  206. delta = 0;
  207. bias = initial_bias;
  208. // Handle the basic code points:
  209. for (j = 0; j < input_length; ++j) {
  210. if ( input[j] < 0x80) {
  211. output.push(
  212. String.fromCharCode(
  213. case_flags ? encode_basic(input[j], case_flags[j]) : input[j]
  214. )
  215. );
  216. }
  217. }
  218. h = b = output.length;
  219. // h is the number of code points that have been handled, b is the
  220. // number of basic code points
  221. if (b > 0) output.push(delimiter);
  222. // Main encoding loop:
  223. //
  224. while (h < input_length) {
  225. // All non-basic code points < n have been
  226. // handled already. Find the next larger one:
  227. for (m = maxint, j = 0; j < input_length; ++j) {
  228. ijv = input[j];
  229. if (ijv >= n && ijv < m) m = ijv;
  230. }
  231. // Increase delta enough to advance the decoder's
  232. // <n,i> state to <m,0>, but guard against overflow:
  233. if (m - n > Math.floor((maxint - delta) / (h + 1))) {
  234. throw RangeError("punycode_overflow (1)");
  235. }
  236. delta += (m - n) * (h + 1);
  237. n = m;
  238. for (j = 0; j < input_length; ++j) {
  239. ijv = input[j];
  240. if (ijv < n ) {
  241. if (++delta > maxint) return Error("punycode_overflow(2)");
  242. }
  243. if (ijv == n) {
  244. // Represent delta as a generalized variable-length integer:
  245. for (q = delta, k = base; ; k += base) {
  246. t = k <= bias ? tmin : k >= bias + tmax ? tmax : k - bias;
  247. if (q < t) break;
  248. output.push( String.fromCharCode(encode_digit(t + (q - t) % (base - t), 0)) );
  249. q = Math.floor( (q - t) / (base - t) );
  250. }
  251. output.push( String.fromCharCode(encode_digit(q, preserveCase && case_flags[j] ? 1:0 )));
  252. bias = adapt(delta, h + 1, h == b);
  253. delta = 0;
  254. ++h;
  255. }
  256. }
  257. ++delta, ++n;
  258. }
  259. return output.join("");
  260. }
  261. this.ToASCII = function ( domain ) {
  262. var domain_array = domain.split(".");
  263. var out = [];
  264. for (var i=0; i < domain_array.length; ++i) {
  265. var s = domain_array[i];
  266. out.push(
  267. s.match(/[^A-Za-z0-9-]/) ?
  268. "xn--" + punycode.encode(s) :
  269. s
  270. );
  271. }
  272. return out.join(".");
  273. }
  274. this.ToUnicode = function ( domain ) {
  275. var domain_array = domain.split(".");
  276. var out = [];
  277. for (var i=0; i < domain_array.length; ++i) {
  278. var s = domain_array[i];
  279. out.push(
  280. s.match(/^xn--/) ?
  281. punycode.decode(s.slice(4)) :
  282. s
  283. );
  284. }
  285. return out.join(".");
  286. }
  287. }();