var unreserved = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-_.~";
var reserved = "!*'();:@&=+$,/?%#[]";
var allowed = unreserved + reserved;
var hexchars = "0123456789ABCDEFabcdef";
// --------------------------------- Encoding -------------------------------
function gethex(decimal) {
  return "%" + hexchars.charAt(decimal >> 4) + hexchars.charAt(decimal & 0xF);
  }

function encode(input) {
  var decoded = input;
  var encoded = "";

    for (var i = 0; i < decoded.length; i++ ) {
      var ch = decoded.charAt(i);
      if (unreserved.indexOf(ch) != -1) {
        encoded = encoded + ch;
      } else {
        var charcode = decoded.charCodeAt(i);
        if (charcode < 128) {
          encoded = encoded + gethex(charcode);
        }
        if (charcode > 127 && charcode < 2048) {
          encoded = encoded + gethex((charcode >> 6) | 0xC0);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }
        if (charcode > 2047 && charcode < 65536) {
          encoded = encoded + gethex((charcode >> 12) | 0xE0);
          encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }
        if (charcode > 65535) {
          encoded = encoded + gethex((charcode >> 18) | 0xF0);
          encoded = encoded + gethex(((charcode >> 12) & 0x3F) | 0x80);
          encoded = encoded + gethex(((charcode >> 6) & 0x3F) | 0x80);
          encoded = encoded + gethex((charcode & 0x3F) | 0x80);
        }
      }
    }
	return encoded;
}
// --------------------------------- Decoding -------------------------------
function getdec(hexencoded) {
  if (hexencoded.length == 3) {
    if (hexencoded.charAt(0) == "%") {
      if (hexchars.indexOf(hexencoded.charAt(1)) != -1 && hexchars.indexOf(hexencoded.charAt(2)) != -1) {
        return parseInt(hexencoded.substr(1,2),16);
      }
    }
  }
  return 256;
}
function decode(input) {
  var encoded = input;
  var decoded = "";
  var notallowed = "";
  var illegalencoding = "";
    var byte1, byte2, byte3, byte4 = 0;
    var i = 0;
    while (i < encoded.length) {
      var ch = encoded.charAt(i);
      if (ch == "%") {
        if (getdec(encoded.substr(i,3)) < 255) {
          byte1 = getdec(encoded.substr(i,3));
          byte2 = getdec(encoded.substr(i+3,3));
          byte3 = getdec(encoded.substr(i+6,3));
          byte4 = getdec(encoded.substr(i+9,3));
          if (byte1 < 128) {
            decoded = decoded + String.fromCharCode(byte1);
            i = i + 3;
          }
          if (byte1 > 127 && byte1 < 192) {
            decoded = decoded + encoded.substr(i,3);
            illegalencoding = illegalencoding + encoded.substr(i,3) + " ";
            i = i + 3;
          }
          if (byte1 > 191 && byte1 < 224) {
            if (byte2 > 127 && byte2 < 192) {
              decoded = decoded + String.fromCharCode(((byte1 & 0x1F) << 6) | (byte2 & 0x3F));
            } else {
              decoded = decoded + encoded.substr(i,6);
              illegalencoding = illegalencoding + encoded.substr(i,6) + " ";
            }
            i = i + 6;
          }
          if (byte1 > 223 && byte1 < 240) {
            if (byte2 > 127 && byte2 < 192) {
              if (byte3 > 127 && byte3 < 192) {
                decoded = decoded + String.fromCharCode(((byte1 & 0xF) << 12) | ((byte2 & 0x3F) << 6) | (byte3 & 0x3F));
              } else {
                decoded = decoded + encoded.substr(i,9);
                illegalencoding = illegalencoding + encoded.substr(i,9) + " ";
              }
            } else {
              decoded = decoded + encoded.substr(i,9);
              illegalencoding = illegalencoding + encoded.substr(i,9) + " ";
            }
            i = i + 9;
          }
          if (byte1 > 239) {
            if (byte2 > 127 && byte2 < 192) {
              if (byte3 > 127 && byte3 < 192) {
                if (byte4 > 127 && byte4 < 192) {
                  decoded = decoded + String.fromCharCode(((byte1 & 0x7) << 18) | ((byte2 & 0x3F) << 12) | ((byte3 & 0x3F) << 6) | (byte4 & 0x3F));
                } else {
                  decoded = decoded + encoded.substr(i,12);
                  illegalencoding = illegalencoding + encoded.substr(i,12) + " ";
                }
              } else {
                decoded = decoded + encoded.substr(i,12);
                illegalencoding = illegalencoding + encoded.substr(i,12) + " ";
              }
            } else {
              decoded = decoded + encoded.substr(i,12);
              illegalencoding = illegalencoding + encoded.substr(i,12) + " ";
            }
            i = i + 12;
          }
        } else {
          decoded = decoded + encoded.substr(i,3);
          illegalencoding = illegalencoding + encoded.substr(i,3) + " ";
          i = i + 3;
        }

      } else {
        if (allowed.indexOf(ch) == -1) notallowed = notallowed + ch + " ";
        decoded = decoded + ch;
        i++;
      }
    }
    var warning = "";
    if (notallowed != "") warning = warning + "Characters not allowed in a URL:\n" + notallowed + "\n\n";
    if (illegalencoding != "") warning = warning + "Illegal percent-encoding (for UTF-8):\n" + illegalencoding  + "\n\n";
    if (warning != "") alert("Warning: Illegal characters/strings in encoded text!\n\n" + warning);
    return decoded;
}