/*
Original:  Tomislav Sereg (tsereg@net.hr) 
Web Site:  http://www.inet.hr/~tsereg/jse 
*/
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);
  var i=0;
  for (i = 0; i < this.nNumElements; i++) {anpermutation[i] = i;}
  for (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;
    var i=0;
    for (i = 0 ; i < nRows; i++) {anRowOfs[ anperm[ i ] ] = i * nCols;}
    var strplain = '';
    for (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);
}



