﻿var Base64Encoder = {
    keyTable: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_=",

    EncodeFirst: function(byte1) {
        return this.keyTable.charAt(byte1 >> 2);
    },
    EncodeSecond: function(byte1, byte2) {
        return this.keyTable.charAt(((byte1 & 3) << 4) | (byte2 >> 4));
    },
    EncodeThird: function(byte2, byte3) {
        return this.keyTable.charAt(((byte2 & 15) << 2) | (byte3 >> 6));
    },
    EncodeForth: function(byte3) {
        return this.keyTable.charAt(byte3 & 63);
    },

    Encode: function(input) {
        var utf8Input = Base64Encoder.Utf8Encode(input);
        var reminder = utf8Input.length % 3;
        var output = "";
        for (var i = 0; i < utf8Input.length - reminder; i += 3) {
            output += Base64Encoder.EncodeFirst(utf8Input.charCodeAt(i))
                + Base64Encoder.EncodeSecond(utf8Input.charCodeAt(i), utf8Input.charCodeAt(i + 1))
                + Base64Encoder.EncodeThird(utf8Input.charCodeAt(i + 1), utf8Input.charCodeAt(i + 2))
                + Base64Encoder.EncodeForth(utf8Input.charCodeAt(i + 2));
        }
        if (reminder == 1) {
            output += Base64Encoder.EncodeFirst(utf8Input.charCodeAt(utf8Input.length - 1))
                    + Base64Encoder.EncodeSecond(utf8Input.charCodeAt(utf8Input.length - 1), 0)
                    + this.keyTable.charAt(64) + this.keyTable.charAt(64);
        }
        if (reminder == 2) {
            output += Base64Encoder.EncodeFirst(utf8Input.charCodeAt(utf8Input.length - 2))
                    + Base64Encoder.EncodeSecond(utf8Input.charCodeAt(utf8Input.length - 2), utf8Input.charCodeAt(utf8Input.length - 1))
                    + Base64Encoder.EncodeThird(utf8Input.charCodeAt(utf8Input.length - 1), 0)
                    + this.keyTable.charAt(64);
        }
        return output;
    },

    Decode: function(input) {
        var inputNorm = input.replace(/[^A-Za-z0-9_\-\=]/g, "");
        var inputLength = inputNorm.length - inputNorm.length % 4;
        var outputLength = inputLength / 4 * 3;
        var output = "";
        for (var i = 0; i < inputLength; i += 4) {
            var enc1 = this.keyTable.indexOf(inputNorm.charAt(i));
            var enc2 = this.keyTable.indexOf(inputNorm.charAt(i + 1));
            var enc3 = this.keyTable.indexOf(inputNorm.charAt(i + 2));
            var enc4 = this.keyTable.indexOf(inputNorm.charAt(i + 3));

            output += String.fromCharCode((enc1 << 2) | (enc2 >> 4));
            if (enc3 != 64) {
                output += String.fromCharCode(((enc2 & 15) << 4) | (enc3 >> 2));
                if (enc4 != 64) {
                    output += String.fromCharCode(((enc3 & 3) << 6) | enc4);
                }
            }
        }
        return Base64Encoder.Utf8Decode(output);
    },

    Utf8Encode: function(string) {
        string = string.replace(/\r\n/g, "\n");
        var utftext = "";

        for (var i = 0; i < string.length; i++) {
            var c = string.charCodeAt(i);

            if (c < 128) {
                utftext += String.fromCharCode(c);
            }
            else if ((c >= 128) && (c < 2048)) {
                utftext += String.fromCharCode((c >> 6) | 192);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else if ((c >= 2048) && (c < 65536)) {
                utftext += String.fromCharCode((c >> 12) | 224);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
            else {
                utftext += String.fromCharCode((c >> 18) | 240);
                utftext += String.fromCharCode(((c >> 12) & 63) | 128);
                utftext += String.fromCharCode(((c >> 6) & 63) | 128);
                utftext += String.fromCharCode((c & 63) | 128);
            }
        }
        return utftext;
    },

    Utf8Decode: function(utftext) {
        var string = "";
        var i = 0;

        while (i < utftext.length) {
            var c = utftext.charCodeAt(i);

            if (c < 128) {
                string += String.fromCharCode(c);
                i++;
            }
            else if ((c > 191) && (c < 224)) {
                if ((i + 1) >= utftext.length) break;
                var c2 = utftext.charCodeAt(i + 1);
                string += String.fromCharCode(((c & 31) << 6) | (c2 & 63));
                i += 2;
            }
            else if ((c > 224) && (c < 240)) {
                if ((i + 2) >= utftext.length) break;
                var c2 = utftext.charCodeAt(i + 1);
                var c3 = utftext.charCodeAt(i + 2);
                string += String.fromCharCode(((c & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
                i += 3;
            }
            else {
                if ((i + 3) >= utftext.length) break;
                var c2 = utftext.charCodeAt(i + 1);
                var c3 = utftext.charCodeAt(i + 2);
                var c4 = utftext.charCodeAt(i + 3);
                string += String.fromCharCode(((c & 7) << 18) | ((c2 & 63) << 12) | ((c3 & 63) << 6) | (c4 & 63));
            }
        }
        return string;
    }
}

