﻿function base64ToAscii(c) {
    var theChar = 0;

    if (0 <= c && c <= 25) {
        theChar = String.fromCharCode(c + 65);
    }
    else if (26 <= c && c <= 51) {
        theChar = String.fromCharCode(c - 26 + 97);
    }
    else if (52 <= c && c <= 61) {
        theChar = String.fromCharCode(c - 52 + 48);
    }
    else if (c == 62) {
        theChar = '+';
    }
    else if (c == 63) {
        theChar = '/';
    }
    else {
        theChar = String.fromCharCode(0xFF);
    }

    return theChar;
}


/**
* Encodes a string according to "URL encoding" rules whereby every
* character is converted to the %xx equivelent. This is similar
* the JavaScript 'escape()' function, except that it encodes all
* the characters, not just the special ones like "-/?&...". For example,
* the string "foo@example.com" will be encoded into 
* "%66%6F%6F%40%65%78%61%6D%70%6C%65%2E%63%6F%6D".
*@see myUrlDecode
*/
function EncodeBase64(str) {
    var result = "";
    var i = 0;
    var sextet = 0;
    var leftovers = 0;
    var octet = 0;

    for (i = 0; i < str.length; i++) {
        octet = str.charCodeAt(i);
        switch (i % 3) {
            case 0:
                {
                    sextet = (octet & 0xFC) >> 2;
                    leftovers = octet & 0x03;

                    // sextet contains first character in quadruple
                    break;
                }

            case 1:
                {
                    sextet = (leftovers << 4) | ((octet & 0xF0) >> 4);
                    leftovers = octet & 0x0F;

                    // sextet contains 2nd character in quadruple
                    break;
                }

            case 2:
                {
                    sextet = (leftovers << 2) | ((octet & 0xC0) >> 6);
                    leftovers = (octet & 0x3F);

                    // sextet contains third character in quadruple
                    // leftovers contains fourth character in quadruple
                    break;
                }
        }

        result = result + base64ToAscii(sextet);

        // don't forget about the fourth character if it is there
        if ((i % 3) == 2) {
            result = result + base64ToAscii(leftovers);
        }

    }

    // figure out what to do with leftovers and padding
    switch (str.length % 3) {
        case 0:
            {
                // an even multiple of 3, nothing left to do
                break;
            }
        case 1:
            {
                // one 6-bit chars plus 2 leftover bits
                leftovers = leftovers << 4;
                result = result + base64ToAscii(leftovers);
                result = result + "==";
                break;
            }
        case 2:
            {
                // two 6-bit chars plus 4 leftover bits
                leftovers = leftovers << 2;
                result = result + base64ToAscii(leftovers);
                result = result + "=";
                break;
            }
    }

    return result;
}


/** 
* A helper function for converting a hex digit into the corresponding
* integer value. I really don't know much about JavaScript, so the
* the conversion is a little laborious. There probably is an easier
* way (including a built-in function), but I don't know it.
*/
function hexval(c) {
    if (String('0').charCodeAt(0) <= c && c <= String('9').charCodeAt(0))
        return c - String('0').charCodeAt(0);
    if (String('A').charCodeAt(0) <= c && c <= String('F').charCodeAt(0))
        return c - String('A').charCodeAt(0) + 10;
    if (String('a').charCodeAt(0) <= c && c <= String('f').charCodeAt(0))
        return c - String('a').charCodeAt(0) + 10;
    return 0;
}

/**
* Decodes a string that has been previously encoded according to the
* myUrlDecode() function.
*@see myUrlEncode
*/
function myBase64Decode(str, is_binary) {
    var result = "";
    var i = 0;
    var x;
    var shiftreg = 0;
    var count = -1;


    for (i = 0; i < str.length; i++) {
        c = str.charAt(i);
        if ('A' <= c && c <= 'Z')
            x = str.charCodeAt(i) - 65;
        else if ('a' <= c && c <= 'z')
            x = str.charCodeAt(i) - 97 + 26;
        else if ('0' <= c && c <= '9')
            x = str.charCodeAt(i) - 48 + 52;
        else if (c == '+')
            x = 62;
        else if (c == '/')
            x = 63;
        else
            continue;

        count++;

        switch (count % 4) {
            case 0:
                shiftreg = x;
                continue;
            case 1:
                v = (shiftreg << 2) | (x >> 4);
                shiftreg = x & 0x0F;
                break;
            case 2:
                v = (shiftreg << 4) | (x >> 2);
                shiftreg = x & 0x03;
                break;
            case 3:
                v = (shiftreg << 6) | (x >> 0);
                shiftreg = x & 0x00;
                break;
        }

        if (!is_binary && (v < 32 || v > 126) && (v != 0x0d) && (v != 0x0a)) {
            result = result + "<";
            result = result + "0123456789ABCDEF".charAt((v / 16) & 0x0F);
            result = result + "0123456789ABCDEF".charAt((v / 1) & 0x0F);
            result = result + ">";
        }
        else
            result = result + String.fromCharCode(v);

    }
    return result.toString();
}

