Minimal Base64 With Unicode Support

btoa(unescape(encodeURIComponent("א")))
===> btoa("א")
===> "15A="

decodeURIComponent(escape(atob("15A=")))
===> decodeURIComponent(escape("א"))
===> "א"

JavaScript Ninja – Unicode Aware Base64

I think examples are more effective than a long explanation.

Have a look at the code below:

(function(window, document, username_placeover_query, password_placeover_query, USERNAME, PASSWORD){
  "use strict";
  
/*┌─────────────────────────────────────────────────────────────────────────────┐
  │ NOTE.                                                                       │
  ╞═════════════════════════════════════════════════════════════════════════════╡
  │- unescape-encodeURIComponent breaks Unicode to binary-string where each     │
  │   character is less than 256 bytes.                                         │
  │- decodeURIComponent-escape brings back binary-string to Unicode.            │
  │- both are consider "hacks/non-official tricks" but are extreamly efficient, │
  │   and 100% compatible with non-Unicode characters.                          │
  └─────────────────────────────────────────────────────────────────────────────┘*/

/*┌─────┐
  │ LIB │
  └─────┘*/
  NodeList.prototype.forEach = Array.prototype.forEach;

  /*
  function base64_encode(str){
    str = unescape(encodeURIComponent(str));
    return btoa(str);
  };
  */

  function base64_decode(str){
    str = decodeURIComponent(escape(str));
    return atob(str);
  };

/*┌──────┐
  │ MAIN │
  └──────┘*/
  document.querySelectorAll(username_placeover_query).forEach(function(element){ element.value = base64_decode(USERNAME); });
  document.querySelectorAll(password_placeover_query).forEach(function(element){ element.value = base64_decode(PASSWORD); });


/*┌─────┐
  │ END │
  └─────┘*/
}(
   top
,  document
,  "#username"
,  "#password"
,  "bXlfdXNlcm5hbWU="   /*= my_username */
,  "bXlfcGFzc3dvcmQ="   /*= my_password */
));

This was intended to be used (after minified) as a bookmarklet,
After you will edit the USERNAME‘s value and PASSWORD‘s value,
and the placeover query,
you could (in a click) fill up username and password in websites that does not support Google Chrome’s authentication sync (or if for example this is the first time after installing new browser, with no previous profile existing on the current OS, or if you just have bookmarks in a html backup file… anyway.. I’ve used it from time to time [make sure to keep this stuff secure though..])

Anyway.. clicking the bookmarklet will fill up the data in the field’s value attribute.

naturally you can provide a better mechanisem by generating mouse click/down/up event or even a char-by-char keyboard “typing” with focus but I’ve skipped this part to simplify the example..

Base64’s btoa and atob methods are supported by mostly all of the new browsers,
which makes the entire solution much shorter than before,

just fyi.. those were the code segment previously required (old browsers) to do the same *thing*:

function base64_decode(data) {
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var o1, o2, o3, h1, h2, h3, h4, bits, i = 0
    , ac = 0
    , dec = ""
    , tmp_arr = [];
  if (!data) {
    return data;
  }
  data += '';

  do {
    h1 = b64.indexOf(data.charAt(i++));
    h2 = b64.indexOf(data.charAt(i++));
    h3 = b64.indexOf(data.charAt(i++));
    h4 = b64.indexOf(data.charAt(i++));
    bits = h1 << 18 | h2 << 12 | h3 << 6 | h4;
    o1 = bits >> 16 & 0xff;
    o2 = bits >> 8 & 0xff;
    o3 = bits & 0xff;
    if (h3 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1);
    } else if (h4 == 64) {
      tmp_arr[ac++] = String.fromCharCode(o1, o2);
    } else {
      tmp_arr[ac++] = String.fromCharCode(o1, o2, o3);
    }
  } while (i < data.length);
  dec = tmp_arr.join('');
  dec = this.utf8_decode(dec);
  return dec;
}

function utf8_decode(str_data) {
  var tmp_arr = []
    , i = 0
    , ac = 0
    , c1 = 0
    , c2 = 0
    , c3 = 0;
  str_data += '';
  while (i < str_data.length) {
    c1 = str_data.charCodeAt(i);
    if (c1 < 128) {
      tmp_arr[ac++] = String.fromCharCode(c1);
      i++;
    } else if (c1 > 191 && c1 < 224) {
      c2 = str_data.charCodeAt(i + 1);
      tmp_arr[ac++] = String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
      i += 2;
    } else {
      c2 = str_data.charCodeAt(i + 1);
      c3 = str_data.charCodeAt(i + 2);
      tmp_arr[ac++] = String.fromCharCode(((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63));
      i += 3;
    }
  }
  return tmp_arr.join('');
}

function utf8_encode(argString) {
  if (argString === null || typeof argString === "undefined") {
    return "";
  }
  var string = (argString + '');
  var utftext = ""
    , start, end, stringl = 0;
  start = end = 0;
  stringl = string.length;
  for (var n = 0; n < stringl; n++) {
    var c1 = string.charCodeAt(n);
    var enc = null;
    if (c1 < 128) {
      end++;
    } else if (c1 > 127 && c1 < 2048) {
      enc = String.fromCharCode((c1 >> 6) | 192) + String.fromCharCode((c1 & 63) | 128);
    } else {
      enc = String.fromCharCode((c1 >> 12) | 224) + String.fromCharCode(((c1 >> 6) & 63) | 128) + String.fromCharCode((c1 & 63) | 128);
    }
    if (enc !== null) {
      if (end > start) {
        utftext += string.slice(start, end);
      }
      utftext += enc;
      start = end = n + 1;
    }
  }
  if (end > start) {
    utftext += string.slice(start, stringl);
  }
  return utftext;
}

function base64_encode(data) {
  var b64 = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
  var o1, o2, o3, h1, h2, h3, h4, bits, i = 0
    , ac = 0
    , enc = ""
    , tmp_arr = [];
  if (!data) {
    return data;
  }
  data = this.utf8_encode(data + '');
  do {
    o1 = data.charCodeAt(i++);
    o2 = data.charCodeAt(i++);
    o3 = data.charCodeAt(i++);
    bits = o1 << 16 | o2 << 8 | o3;
    h1 = bits >> 18 & 0x3f;
    h2 = bits >> 12 & 0x3f;
    h3 = bits >> 6 & 0x3f;
    h4 = bits & 0x3f;
    tmp_arr[ac++] = b64.charAt(h1) + b64.charAt(h2) + b64.charAt(h3) + b64.charAt(h4);
  } while (i < data.length);
  enc = tmp_arr.join('');
  var r = data.length % 3;
  return (r ? enc.slice(0, r - 3) : enc) + '==='.slice(r || 3);
}

by the way here is a binary string – Unicode examples

unescape(encodeURIComponent("א"))  -to- "א"   to binary string (length=1 -to- lengh=2)
decodeURIComponent(escape("א"))    -to- "א"   from binary string (length=2 - to lengh=1)