// JavaScript Document
//-- Original:  Tomislav Sereg (tsereg@net.hr) -->
//-- Web Site:  http://www.inet.hr/~tsereg/jse -->

//-- This script and many more are available free online at -->
//-- The JavaScript Source!! http://javascript.internet.com -->

function permutationGenerator(nNumElements)
{
	this.nNumElements     = nNumElements;
	this.antranspositions = new Array;
	var k = 0;
	for (i = 0; i < nNumElements - 1; i++)
		for (j = i + 1; j < nNumElements; j++)
			this.antranspositions[ k++ ] = ( i << 8 ) | j;
	// keep two positions as lo and hi byte!
	this.nNumtranspositions = k;
	this.fromCycle = permutationGenerator_fromCycle;
}

function permutationGenerator_fromCycle(anCycle)
{
	var anpermutation = new Array(this.nNumElements);
	for (var i = 0; i < this.nNumElements; i++) anpermutation[i] = i;
	for (var i = 0; i < anCycle.length; i++)
	{
		var nT = this.antranspositions[anCycle[i]];
		var n1 = nT & 255;
		var n2 = (nT >> 8) & 255;
		nT = anpermutation[n1];
		anpermutation[n1] = anpermutation[n2];
		anpermutation[n2] = nT;
	}
	return anpermutation;
}

function password(strpasswd)
{
	this.strpasswd = strpasswd;
	this.getHashValue   = password_getHashValue;
	this.getpermutation = password_getpermutation;
}

function password_getHashValue()
{
	var m = 907633409;
	var a = 65599;
	var h = 0;
	for (var i = 0; i < this.strpasswd.length; i++) 
		h = (h % m) * a + this.strpasswd.charCodeAt(i);
	return h;
}

function password_getpermutation()
{
	var nNUMELEMENTS = 13;
	var nCYCLELENGTH = 21;
	pg = new permutationGenerator(nNUMELEMENTS);
	var anCycle = new Array(nCYCLELENGTH);
	var npred   = this.getHashValue();
	for (var i = 0; i < nCYCLELENGTH; i++)
	{
		npred = 314159269 * npred + 907633409;
		anCycle[i] = npred % pg.nNumtranspositions;
	}
	return pg.fromCycle(anCycle);
}

function SecureContext(strText, strSignature, bEscape)
{
	this.strSIGNATURE = strSignature || '';
	this.bESCApE      = bEscape || false;
	this.strText = strText;
	this.escape        = SecureContext_escape;
	this.unescape      = SecureContext_unescape;
	this.transliterate = SecureContext_transliterate;
	this.encypher      = SecureContext_encypher;
	this.decypher      = SecureContext_decypher;
	this.sign          = SecureContext_sign;
	this.unsign        = SecureContext_unsign;
	this.secure   = SecureContext_secure;
	this.unsecure = SecureContext_unsecure;
}

function SecureContext_escape(strToEscape)
{
	var strEscaped = '';
	for (var i = 0; i < strToEscape.length; i++)
	{
		var chT = strToEscape.charAt( i );
		switch(chT)
		{
case '\r': strEscaped += '\\r'; break;
case '\n': strEscaped += '\\n'; break;
case '\\': strEscaped += '\\\\'; break;
default: strEscaped += chT;
   		}
	}
	return strEscaped;
}

function SecureContext_unescape(strToUnescape) {
var strUnescaped = '';
var i = 0;
while (i < strToUnescape.length) {
var chT = strToUnescape.charAt(i++);
if ('\\' == chT) {
chT = strToUnescape.charAt( i++ );
switch( chT ) {
case 'r': strUnescaped += '\r'; break;
case 'n': strUnescaped += '\n'; break;
case '\\': strUnescaped += '\\'; break;
default: // not possible
   }
}
else strUnescaped += chT;
}
return strUnescaped;
}
function SecureContext_transliterate(btransliterate) {
var strDest = '';

var nTextIter  = 0;
var nTexttrail = 0;

while (nTextIter < this.strText.length) {
var strRun = '';
var cSkipped   = 0;
while (cSkipped < 7 && nTextIter < this.strText.length) {
var chT = this.strText.charAt(nTextIter++);
if (-1 == strRun.indexOf(chT)) {
strRun += chT;
cSkipped = 0;
}
else cSkipped++;
}
while (nTexttrail < nTextIter) {
var nRunIdx = strRun.indexOf(this.strText.charAt(nTexttrail++));
if (btransliterate) {
nRunIdx++
if (nRunIdx == strRun.length) nRunIdx = 0;
}
else {
nRunIdx--;
if (nRunIdx == -1) nRunIdx += strRun.length;
}
strDest += strRun.charAt(nRunIdx);
   }
}
this.strText = strDest;
}
function SecureContext_encypher(anperm) {
var strEncyph = '';
var nCols     = anperm.length;
var nRows     = this.strText.length / nCols;
for (var i = 0; i < nCols; i++) {
var k = anperm[ i ];
for (var j = 0; j < nRows; j++) {
strEncyph += this.strText.charAt(k);
k         += nCols;
   }
}
this.strText = strEncyph;
}
function SecureContext_decypher(anperm) {
var nRows    = anperm.length;
var nCols    = this.strText.length / nRows;
var anRowOfs = new Array;
for (var i = 0 ; i < nRows; i++) anRowOfs[ anperm[ i ] ] = i * nCols;
var strplain = '';
for (var i = 0; i < nCols; i++) {
for (var j = 0; j < nRows; j++)
strplain += this.strText.charAt(anRowOfs[ j ] + i);
}
this.strText = strplain;
}
function SecureContext_sign(nCols) {
if (this.bESCApE) {
this.strText      = this.escape(this.strText);
this.strSIGNATURE = this.escape(this.strSIGNATURE);
}
var nTextLen     = this.strText.length + this.strSIGNATURE.length;
var nMissingCols = nCols - (nTextLen % nCols);
var strpadding   = '';  
if (nMissingCols < nCols)
for (var i = 0; i < nMissingCols; i++) strpadding += ' ';
var x = this.strText.length;
this.strText +=  strpadding + this.strSIGNATURE;
}
function SecureContext_unsign(nCols) {
if (this.bESCApE) {
this.strText      = this.unescape(this.strText);
this.strSIGNATURE = this.unescape(this.strSIGNATURE);
}
if ('' == this.strSIGNATURE) return true;
var nTextLen = this.strText.lastIndexOf(this.strSIGNATURE);
if (-1 == nTextLen) return false;
this.strText = this.strText.substr(0, nTextLen);
return true;
}

function SecureContext_secure(strpasswd) {
var passwd = new password(strpasswd);
var anperm   = passwd.getpermutation()
this.sign(anperm.length);
this.transliterate(true);
this.encypher(anperm);
}

function SecureContext_unsecure(strpasswd) {
var passwd = new password(strpasswd);
var anperm = passwd.getpermutation()
this.decypher(anperm);
this.transliterate(false);
return this.unsign(anperm.length);
}

var use_newlines = false;
var signature = "ManywillseeandfearandputtheirtrustintheLord";
var mypassword = "Psalm132";

function doSecure(s)
{
	var sc = new SecureContext(s, 
								signature,
								false);
	sc.secure(mypassword);
	return sc.strText;
}

function doUnsecure(encoded_text)
{
	var sc = new SecureContext(encoded_text, 
							   signature, 
								use_newlines);
	if (!sc.unsecure(mypassword)) 
		return null;

	return sc.strText;
}
