WHCSetup();
if (navigator.userAgent.indexOf("Gecko/") != -1) {
	setTimeout("WHCInit();", 1000);
} else {
	document.body.onload = WHCInit;
}

function WHCSetup() {
	if ((navigator.userAgent.indexOf("MSIE") != -1) && (navigator.platform.indexOf("Mac") != -1)) {
		window.WHCUndefined = null;
	} else {
		window.WHCUndefined = undefined;
	}
	window.WHCResource = "";
	window.WHCPostage = 22;
	window.WHCAppletDirectory = "";
	window.WHCWorkingMessage = "Generating stamp";
	window.WHCFinishedMessage = "Stamp applied.";
	window.WHCManualFinishedMessage = "Type these characters exactly as they appear:";
	window.WHCErrorMessage = "Failed to generate stamp.";
	window.WHCNeedStamp = true;
	window.WHCShowStampField = false;
	
	window.WHCDefaultPostage = 22;
	window.WHCFlasher = 0;
	window.WHCWaitInterval = 250;
	window.WHCLiveConnectMode = false;
	
	document.write('[<a href="http://www.davidsj.com/webhashcash/what.php" target="_blank">?</a>]');
	if (window.WHCFakeForm) {
		document.write(' [<a href="javascript:;">&gt;</a>] ');
	} else {
		document.write(' [<a href="javascript:WHCReset();">&gt;</a>] ');
	}
	document.write('<span id="WHCStampStatus"></span><span id="WHCAppletSpan"></span>');
	document.write('<span id="WHCStampAfter" style="display: none;"><input name="WHCStampSuffix" id="WHCStampSuffix" type="text" size="5" onKeyUp="WHCUpdateSuffix();"></span>');
	document.write('<span id="WHCHiddenStampSpan"></span>');
	document.write('<input name="WHCT" id="WHCT" type="hidden">');
	if (window.WHCFakeForm) {
		WHCAnimateFakeForm();
	}
}

function WHCAnimateFakeForm() {
	WHCUpdateTimeRemaining(3);
	setTimeout("WHCAnimateFakeForm();", 250);
}

function WHCStyleAttr(attr) {
	theElement = document.getElementById('WHCAppletSpan');
	do {
		theAttr = theElement.style[attr];
		if (theElement == document.body) {
			break;
		} else {
			theElement = theElement.parentNode;
		}
	} while (!theAttr);
	return theAttr;
}

function WHCInit() {
	if (window.WHCFakeForm) {
		return;
	}
	
	if ((!window.WHCAfterPresent) && (!window.WHCConfig)) {
		setTimeout("WHCInit();", 1000);
	} else if (window.WHCConfig) {
		WHCConfig();
	}
	
	if ((window.WHCSubmitButton == window.WHCUndefined)) {
		for (i = 0; i < document.forms.length; i++) {
			thisForm = document.forms[i];
			for (j = 0; j < thisForm.length; j++) {
				thisObject = thisForm[j];
				if (thisObject.type == 'submit') {
					thisName = thisObject.name;
					thisValue = thisObject.value;
					if ((thisName.toLowerCase().indexOf('submit') != -1) || (thisName.toLowerCase().indexOf('post') != -1) || (thisName.toLowerCase().indexOf('send') != -1)) {
						window.WHCSubmitButton = thisObject;
					} else if ((thisValue.toLowerCase().indexOf('submit') != -1) || (thisValue.toLowerCase().indexOf('post') != -1) || (thisValue.toLowerCase().indexOf('send') != -1)) {
						window.WHCSubmitButton = thisObject;
					}
				}
			}
		}
	}
	if (window.WHCShowStampField) {
		document.getElementById('WHCVisibleStampSpan').innerHTML = '<input name="WHCStamp" id="WHCStamp" type="text" size="80">';
	} else {
		document.getElementById('WHCHiddenStampSpan').innerHTML = '<input name="WHCStamp" id="WHCStamp" type="hidden">';
	}
	window.WHCExpectedTries = Math.pow(2, window.WHCPostage);
	window.WHCExpectedManualSuffixLength = Math.ceil(window.WHCPostage / 5) + 1;
	window.WHCExpectedGenerationTime = Math.pow(2, window.WHCPostage-16);
	
	document.getElementById('WHCStampSuffix').size = window.WHCExpectedManualSuffixLength;
	
	if ((window.WHCAppletDirectory.length > 0) && (WHCAppletDirectory.charAt(window.WHCAppletDirectory.length-1) != "/")) {
		window.WHCAppletDirectory += "/";
	}
	
	window.WHCBackgroundColor = WHCStyleAttr("backgroundColor");
	window.WHCColor = WHCStyleAttr("color");
	window.WHCFontFamily = WHCStyleAttr("fontFamily");
	window.WHCFontSize = WHCStyleAttr("fontSize");
	window.WHCFontWeight = WHCStyleAttr("fontWeight");
	window.WHCFontStyle = WHCStyleAttr("fontStyle");
	window.WHCFontSize = WHCStyleAttr("fontSize");
	if (window.WHCFontSize) {
		fsIndex = 50000;
		if (window.WHCFontSize.indexOf(' ') != -1) {
			fsIndex = Math.min(fsIndex, window.WHCFontSize.indexOf(' '));
		}
		if (window.WHCFontSize.indexOf('pt') != -1) {
			fsIndex = Math.min(fsIndex, window.WHCFontSize.indexOf('pt'));
		}
		if (window.WHCFontSize.indexOf('px') != -1) {
			fsIndex = Math.min(fsIndex, window.WHCFontSize.indexOf('px'));
		}
		if (fsIndex != 50000) {
			window.WHCFontSize = window.WHCFontSize.substring(0, fsIndex);
		}
	} else {
		window.WHCFontSize = 12;
	}
	
	WHCStart();
	if (WHCBrowserDoesJava()) {
		WHCCreateAppletHTML();
	} else {
		window.WHCSubmitButton.disabled = window.WHCNeedStamp;
		document.getElementById('WHCStampStatus').innerHTML = WHCErrorMessage+' &#151; no java [<a href="http://www.davidsj.com/webhashcash/browser_compatibility.php" target="_blank">Browser Compatibility</a>]';
	}
}

function WHCStart() {
	window.WHCFlasher = 0;
	if (window.WHCSubmitButton != window.WHCUndefined) {
		window.WHCSubmitButton.disabled = true;
	}
	document.getElementById('WHCStampSuffix').value = "";
	
	now = new Date();
	window.WHCStartTime = now.getTime();
	
	dateString = "";
	year = now.getUTCFullYear() % 100;
	month = now.getUTCMonth() + 1;
	date = now.getUTCDate();
	hour = now.getUTCHours();
	minute = now.getUTCMinutes();
	seconds = now.getUTCSeconds();
	dateString += (year < 10) ? ("0"+year) : (""+year);
	dateString += (month < 10) ? ("0"+month) : (""+month);
	dateString += (date < 10) ? ("0"+date) : (""+date);
	dateString += (hour < 10) ? ("0"+hour) : (""+hour);
	dateString += (minute < 10) ? ("0"+minute) : (""+minute);
	dateString += (seconds < 10) ? ("0"+seconds) : (""+seconds);
	randString = Math.floor(Math.random()*65536).toString(36);
	window.WHCPrefix = '1:'+window.WHCPostage+':'+dateString+':'+window.WHCResource+'::'+randString+':';
	
	if (WHCBrowserDoesJava()) {
		if (WHCBrowserDoesLiveConnect() == "yes") {
			window.WHCLiveConnectMode = true;
			document.getElementById('WHCStampAfter').style.display = "none";
			document.getElementById('WHCStampStatus').innerHTML = window.WHCWorkingMessage+WHCFlasherString()+'(loading applet)';
		} else if (WHCBrowserDoesLiveConnect() == "no") {
			document.getElementById('WHCStampAfter').style.display = "";
			window.WHCLiveConnectMode = false;
		} else if (WHCBrowserDoesLiveConnect() == "maybe") {
			window.WHCLiveConnectMode = false;
			document.getElementById('WHCStampAfter').style.display = "none";
			setTimeout("WHCWaitForApplet("+(window.WHCWaitInterval)+");", window.WHCWaitInterval);
		}
	} else {
		window.WHCLiveConnectMode = false;
		document.getElementById('WHCStampAfter').style.display = "none";
	}
}

function WHCCreateAppletHTML() {
	appletHTML = '';
	appletHTML += '<applet archive="'+window.WHCAppletDirectory+'WebHashcash.jar" code="WebHashcash" align="middle"';
	if (WHCBrowserDoesLiveConnect() == "yes") {
		appletHTML += ' width="0" height="0"';
	} else {
		appletHTML += ' width="'+WHCAppletWidthForTime(0)+'" height="'+WHCAppletHeightForTime(0)+'"';
	}
	appletHTML += ' name="WHCApplet" id="WHCApplet" mayscript>';
	appletHTML += '<param name="postage" value="'+window.WHCPostage+'">';
	appletHTML += '<param name="resource" value="'+window.WHCResource+'">';
	appletHTML += '<param name="prefix" value="'+window.WHCPrefix+'">';
	if (window.WHCForceManual) {
		appletHTML += '<param name="forceManual" value="true">';
	}
	appletHTML += '<param name="color" value="'+window.WHCColor+'">';
	appletHTML += '<param name="backgroundColor" value="'+window.WHCBackgroundColor+'">';
	appletHTML += '<param name="fontFamily" value="'+window.WHCFontFamily+'">';
	appletHTML += '<param name="fontWeight" value="'+window.WHCFontWeight+'">';
	appletHTML += '<param name="fontStyle" value="'+window.WHCFontStyle+'">';
	appletHTML += '<param name="fontSize" value="'+window.WHCFontSize+'">';
	appletHTML += '<param name="workingMessage" value="'+window.WHCWorkingMessage+'">';
	appletHTML += '<param name="manualFinishedMessage" value="'+window.WHCManualFinishedMessage+'">';
	appletHTML += 'Your browser must have Java enabled in order to use this form.';
	appletHTML += '</applet>';
	
	document.getElementById('WHCAppletSpan').innerHTML = appletHTML;
}

function WHCWaitForApplet(waitTime) {
	if (!window.WHCLiveConnectMode) {
		if (document.applets['WHCApplet'].width != window.WHCUndefined) {
			document.applets['WHCApplet'].width = WHCAppletWidthForTime(waitTime);
			document.applets['WHCApplet'].height = WHCAppletHeightForTime(waitTime);
		}
		if (waitTime < 4000) {
			setTimeout("WHCWaitForApplet("+(waitTime+window.WHCWaitInterval)+");", window.WHCWaitInterval);
		} else {
			WHCEnableManualMode();
		}
	}
}

function WHCAppletHello(ver) {
	if (!window.WHCForceManual) {
		window.WHCAppletVersion = ver;
		if (WHCCheckAppletVersion()) {
			WHC_JSHello();
			WHCEnableLiveConnectMode();
			window.WHCAppletLoadTime = 0;
		}
	}
}

function WHC_JSHello() {
	document.getElementById('WHCStampStatus').innerHTML = window.WHCWorkingMessage+WHCFlasherString()+'(calculating time)';
}

function WHCEnableLiveConnectMode() {
	window.WHCLiveConnectMode = true;
	if (WHCBrowserDoesLiveConnect() == "maybe") {
		if (document.applets['WHCApplet'].width == window.WHCUndefined) {
			document.getElementById('WHCAppletSpan').style.display = "none";
		} else {
			document.applets['WHCApplet'].width = 0;
			document.applets['WHCApplet'].height = 0;
		}
		document.getElementById('WHCStampAfter').style.display = "none";
		if (window.WHCSubmitButton != window.WHCUndefined) {
			window.WHCSubmitButton.disabled = true;
		}
	}
}

function WHCEnableManualMode() {
	window.WHCLiveConnectMode = false;
	if (WHCBrowserDoesLiveConnect() == "maybe") {
		document.getElementById('WHCStampStatus').innerHTML = "";
		/*if (document.applets['WHCApplet'].width == window.WHCUndefined) {
			document.getElementById('WHCAppletSpan').style.display = "";
		} else {
			document.applets['WHCApplet'].width = window.WHCAppletWidth;
			document.applets['WHCApplet'].height = window.WHCAppletHeight;
		}*/
		document.getElementById('WHCStampAfter').style.display = "";
	}
}

function WHCUpdateTimeRemaining(secs) {
	if (!window.WHCForceManual) {
		if (WHCCheckAppletVersion()) {
			WHCEnableLiveConnectMode();
			WHC_JSUpdateTimeRemaining(secs);
		}
	}
}

function WHC_JSUpdateTimeRemaining(secs) {
	secsLeft = Math.round(parseFloat(secs));
	if (secsLeft < 60) {
		timeString = secsLeft+' secs';
	} else if (secsLeft < 3600) {
		timeString = Math.floor(secsLeft / 60)+' mins, '+(secsLeft % 60)+' secs';
	} else {
		timeString = Math.floor(secsLeft / 3600)+' hours, '+(Math.round(secsLeft / 60) % 60)+' mins';
	}
	progressString = window.WHCWorkingMessage+WHCFlasherString()+'(about '+timeString+')';
	document.getElementById('WHCStampStatus').innerHTML = progressString;
}

function WHCApplyPostage(stamp) {
	if (!window.WHCForceManual) {
		if (WHCCheckAppletVersion()) {
			WHCEnableLiveConnectMode();
			WHC_JSApplyPostage(stamp);
		}
	}
}

function WHC_JSApplyPostage(stamp) {
	document.getElementById('WHCStampStatus').innerHTML = window.WHCFinishedMessage;
	if (window.WHCSubmitButton != window.WHCUndefined) {
		window.WHCSubmitButton.disabled = false;
	}
	WHCSetStamp(stamp);
}

function WHCUpdateSuffix() {
	if (document.getElementById('WHCStampSuffix').value != window.WHCStampSuffix) {
		document.getElementById('WHCStampSuffix').value = document.getElementById('WHCStampSuffix').value.toUpperCase();
		window.WHCStampSuffix = document.getElementById('WHCStampSuffix').value;
		testSuffix = document.getElementById('WHCStampSuffix').value.toUpperCase();
		testStamp = window.WHCPrefix + testSuffix;
		if (WHCMeasuredValue(testStamp) >= window.WHCPostage) {
			document.getElementById('WHCAppletSpan').style.display = "none";
			WHC_JSApplyPostage(testStamp);
			document.getElementById("WHCStampAfter").style.display = "none";
		}
	}
}

function WHCSetStamp(stamp) {
	document.getElementById('WHCStamp').value = stamp;
	document.getElementById('WHCT').value = ((new Date()).getTime() - window.WHCStartTime) * 0.001;
}

function WHCFlasherString() {
	flasherChar = '-'
	if (window.WHCFlasher == 0) {
		theResult = flasherChar+'&nbsp;&nbsp;&nbsp;';
		flasherDirection = 1;
	} else if (window.WHCFlasher == 1) {
		theResult = '&nbsp;'+flasherChar+'&nbsp;&nbsp;';
	} else if (window.WHCFlasher == 2) {
		theResult = '&nbsp;&nbsp;'+flasherChar+'&nbsp;';
	} else if (window.WHCFlasher == 3) {
		theResult = '&nbsp;&nbsp;&nbsp;'+flasherChar;
		flasherDirection = -1;
	}
	
	window.WHCFlasher += flasherDirection;
	return ' '+theResult+' ';
}

function WHCReset() {
	WHCStart();
	if (WHCBrowserDoesJava()) {
		if (window.WHCLiveConnectMode) {
			document.applets['WHCApplet'].resetGeneration();
		} else {
			WHCCreateAppletHTML();
		}
	} else {
		window.WHCShouldRun = false;
		alert('Sorry, your browser is incompatible with WebHashcash.');
	}
}

function WHCCheckAppletVersion() {
	if (window.WHCAppletVersion < 0.2) {
		document.getElementById('WHCStampStatus').innerHTML = window.WHCErrorMessage+' &#151; outdated applet';
		return false;
	} else {
		return true;
	}
}

function WHCBrowserDoesJava() {
	return navigator.javaEnabled() || (navigator.platform.indexOf("Mac") != -1);
}

function WHCBrowserDoesLiveConnect() {
	//return "maybe";
	if (window.WHCForceManual) {
		return "no";
	} else {
		if (navigator.userAgent.indexOf("Safari/") != -1) {
			return "yes";
		} else if (navigator.userAgent.indexOf("Gecko/") != -1) {
			return (navigator.platform.indexOf("Mac") == -1) ? "yes" : "no";
		} else if (navigator.userAgent.indexOf("Opera") != -1) {
			return "no";
		} else if (navigator.userAgent.indexOf("MSIE") != -1) {
			return (navigator.platform.indexOf("Mac") == -1) ? "yes" : "no";
		} else {
			return "maybe";
		}
	}
}

function WHCAppletWidthForTime(t) {
	maybeSecsLeft = window.WHCExpectedGenerationTime - (t * 0.001);
	charRoom = window.WHCWorkingMessage.length;
	
	if (window.WHCExpectedGenerationTime < 60) {
		charRoom += 6 + 22;
	} else {
		charRoom += 6 + 24;
	}
	charRoom = Math.max(charRoom, window.WHCManualFinishedMessage.length + window.WHCExpectedManualSuffixLength + 4);
	if (maybeSecsLeft >= 60) {
		widthResult = ((charRoom + 2) / 2) * window.WHCFontSize;
	} else if (maybeSecsLeft > 0) {
		widthResult = ((charRoom + 2) / 2) * window.WHCFontSize;
	} else {
		widthResult = ((charRoom + 2) / 2) * window.WHCFontSize;
	}
	return Math.round(widthResult);
}

function WHCAppletHeightForTime(t) {
	maybeSecsLeft = window.WHCExpectedGenerationTime - (t * 0.001);
	if (maybeSecsLeft >= 60) {
		heightResult = 1.2 * window.WHCFontSize;
	} else if (maybeSecsLeft > 0) {
		heightResult = 1.2 * window.WHCFontSize;
	} else {
		heightResult = 1.2 * window.WHCFontSize;
	}
	return Math.round(heightResult);
}


function WHCMeasuredValue(stamp) {
	hash = hex_sha1(stamp);
	lzb = 0;
	for (i = 0; i < 40; i++) {
		test_char = parseInt(hash.charAt(i), 16);
		if (test_char == 0) {
			lzb += 4;
		} else {
			if (test_char < 2) {
				lzb += 3;
			} else if (test_char < 4) {
				lzb += 2;
			} else if (test_char < 8) {
				lzb += 1;
			}
			break;
		}
	}
	return lzb;
}

// sha1.js included inline for backwards-compatibility

/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Version 2.1 Copyright Paul Johnston 2000 - 2002.
 * Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
 * Distributed under the BSD License
 * See http://pajhome.org.uk/crypt/md5 for details.
 */

/*
 * Configurable variables. You may need to tweak these to be compatible with
 * the server-side, but the defaults work in most cases.
 */
var hexcase = 0;  /* hex output format. 0 - lowercase; 1 - uppercase        */
var b64pad  = ""; /* base-64 pad character. "=" for strict RFC compliance   */
var chrsz   = 8;  /* bits per input character. 8 - ASCII; 16 - Unicode      */

/*
 * These are the functions you'll usually want to call
 * They take string arguments and return either hex or base-64 encoded strings
 */
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}

/*
 * Perform a simple self-test to see if the VM is working
 */
function sha1_vm_test()
{
  return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
}

/*
 * Calculate the SHA-1 of an array of big-endian words, and a bit length
 */
function core_sha1(x, len)
{
  /* append padding */
  x[len >> 5] |= 0x80 << (24 - len % 32);
  x[((len + 64 >> 9) << 4) + 15] = len;

  var w = Array(80);
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for(var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for(var j = 0; j < 80; j++)
    {
      if(j < 16) w[j] = x[i + j];
      else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)), 
                       safe_add(safe_add(e, w[j]), sha1_kt(j)));
      e = d;
      d = c;
      c = rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  return Array(a, b, c, d, e);
  
}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function sha1_ft(t, b, c, d)
{
  if(t < 20) return (b & c) | ((~b) & d);
  if(t < 40) return b ^ c ^ d;
  if(t < 60) return (b & c) | (b & d) | (c & d);
  return b ^ c ^ d;
}

/*
 * Determine the appropriate additive constant for the current iteration
 */
function sha1_kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}  

/*
 * Calculate the HMAC-SHA1 of a key and some data
 */
function core_hmac_sha1(key, data)
{
  var bkey = str2binb(key);
  if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);

  var ipad = Array(16), opad = Array(16);
  for(var i = 0; i < 16; i++) 
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
  return core_sha1(opad.concat(hash), 512 + 160);
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally
 * to work around bugs in some JS interpreters.
 */
function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

/*
 * Bitwise rotate a 32-bit number to the left.
 */
function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}

/*
 * Convert an 8-bit or 16-bit string to an array of big-endian words
 * In 8-bit function, characters >255 have their hi-byte silently ignored.
 */
function str2binb(str)
{
  var bin = Array();
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < str.length * chrsz; i += chrsz)
    bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (24 - i%32);
  return bin;
}

/*
 * Convert an array of big-endian words to a string
 */
function binb2str(bin)
{
  var str = "";
  var mask = (1 << chrsz) - 1;
  for(var i = 0; i < bin.length * 32; i += chrsz)
    str += String.fromCharCode((bin[i>>5] >>> (24 - i%32)) & mask);
  return str;
}

/*
 * Convert an array of big-endian words to a hex string.
 */
function binb2hex(binarray)
{
  var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i++)
  {
    str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
           hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8  )) & 0xF);
  }
  return str;
}

/*
 * Convert an array of big-endian words to a base-64 string
 */
function binb2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for(var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
                | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
                |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
    for(var j = 0; j < 4; j++)
    {
      if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
      else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
    }
  }
  return str;
}
