

/* File: Templates/javascript/http_request.js */
/*
*
* This software may contain computer code that is proprietary to Interactive Brokers LLC
* and may not be copied, distributed or used for any purpose other than the authorized use
* of this software. Copyright, Interactive Brokers LLC (2004). All rights reserved.
*
*/

/**************** CROSS-BROWSER XML HTTP REQUEST AND XML PARSER ***************/

//var loader = new xhr.ContentLoader('GET','test.xml?id=123',true,handleGetResponse);

/* 
Wrapper function for constructing a request object
  Parameters:
	method: POST or GET
	url: local URL
	asynch: true or false
	respHandle: function name to handle response
	5th element: parameters for POST method
*/
var xhr = new Object();
xhr.READY_STATE_UNINITIALIZED = 0;
xhr.READY_STATE_LOADING = 1;
xhr.READY_STATE_LOADED = 2;
xhr.READY_STATE_INTERACTIVE = 3;
xhr.READY_STATE_COMPLETE = 4;
xhr.ContentLoader = function(method,url,asynch,respHandle,params,onerror){
  this.method = 		method;
  this.url = 			  url; 
  this.asynch = 		asynch;  
  this.respHandle = respHandle;
  this.params = 		params;  
  this.onerror = 		(onerror) ? onerror : this.defaultError;
  this.req = 			  null;
  var agent = 			navigator.userAgent.toLowerCase();
  var ie5or6 = 			(agent.indexOf("msie 5")!=-1||agent.indexOf("msie 6")!=-1) ? true : false;
   // alert(ie5or6);
   // alert(method);
   // alert(url);
   // alert(params);
  if(ie5or6==true && method.toLowerCase()=="post"){//IE6 POST will use GET
      
	this.method = method = "GET";
	this.url = url = url + "?" + params;
	this.params = params = null;

  }
  // alert('after');
 //  alert(url);
 // alert(method);
  this.loadXMLDoc(method,url,asynch,params);
}
xhr.ContentLoader.prototype = {
	loadXMLDoc:function(method,url,asynch,params){		
      //Mozilla-based browsers
       if(url.indexOf("?")!=-1){ url += "&"+Math.floor(Math.random()*100001); }
       else{ url += "?"+Math.floor(Math.random()*100001); }

      if(window.XMLHttpRequest){
          this.req = new XMLHttpRequest();
      //Internet Explorer
      }else if(window.ActiveXObject){
          this.req = new ActiveXObject("Msxml2.XMLHTTP");
          if(!this.req){
              this.req = new ActiveXObject("Microsoft.XMLHTTP");
          }
      }
      //test for null request
      if(this.req){
          if(method.toLowerCase() != "post"){
              this.initReq(method,url,asynch);
          }else{//POSTed data
              var args = arguments[3];
              if(args != null && args.length > 0){
                  this.initReq(method,url,asynch,args);
              }
          }
      }else{
          alert("Your browser does not permit the use of all "+
                "of this application's features!");
      }	
	},
	initReq:function(method,url,asynch,args){		
      try{
          //Specify the function which will handle HTTP response
          var loader = this;
          this.req.onreadystatechange = function(){                                
              loader.onReadyState.call(loader);
          }
          this.req.open(method,url,asynch);
          //POSTed data
          if(method.toLowerCase() == "post"){
              this.req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
              this.req.send(arguments[3]);	
          }else{
              this.req.send(null);
          }
      }catch (errv){
          alert("The application cannot contact the server at the moment. Error details: "+errv.message);
      }		
	},
	onReadyState:function(){
      var req = this.req;
      var ready = req.readyState;
      if(ready == xhr.READY_STATE_COMPLETE){        
           var httpStatus = req.status; 
           if(httpStatus == 200 || httpStatus == 0){
               this.respHandle.call(this);               
           }else{
               this.onerror.call(this);
           }
      }
  },
  defaultError:function(){
  /*
      alert("error fetching data!"
      +"\n\nreadyState:"+this.req.readyState
      +"\nstatus: "+this.req.status
      +"\nheaders: "+this.req.getAllResponseHeaders());
      */
      }
}




/* File: Templates/javascript/sha1.js */
/*
 * A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
 * in FIPS PUB 180-1
 * Copyright (C) Paul Johnston 2000.
 * See http://pajhome.org.uk/site/legal.html for details.
 */
/*
 * Modified by Tom Wu (tjw@cs.stanford.edu) for the
 * XYZ JavaScript implementation.
 */

/*
 * Convert a 32-bit number to a hex string with ms-byte first
 */
var hex_chr = "0123456789abcdef";
function hex(num)
{
  var str = "";
  for(var j = 7; j >= 0; j--)
    str += hex_chr.charAt((num >> (j * 4)) & 0x0F);
  return str;
}

/*
 * Convert a string to a sequence of 16-word blocks, stored as an array.
 * Append padding bits and the length, as described in the SHA1 standard.
 */
function str2blks_SHA1(str)
{
  var nblk = ((str.length + 8) >> 6) + 1;
  var blks = new Array(nblk * 16);
  for(var i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(i = 0; i < str.length; i++)
    blks[i >> 2] |= str.charCodeAt(i) << (24 - (i % 4) * 8);
  blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8);
  blks[nblk * 16 - 1] = str.length * 8;
  return blks;
}

/*
 * Input is in hex format - trailing odd nibble gets a zero appended.
 */
function hex2blks_SHA1(hex)
{
  var len = (hex.length + 1) >> 1;
  var nblk = ((len + 8) >> 6) + 1;
  var blks = new Array(nblk * 16);
  for(var i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(i = 0; i < len; i++)
    blks[i >> 2] |= parseInt(hex.substr(2*i, 2), 16) << (24 - (i % 4) * 8);
  blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8);
  blks[nblk * 16 - 1] = len * 8;
  return blks;
}

function ba2blks_SHA1(ba, off, len)
{
  var nblk = ((len + 8) >> 6) + 1;
  var blks = new Array(nblk * 16);
  for(var i = 0; i < nblk * 16; i++) blks[i] = 0;
  for(i = 0; i < len; i++)
    blks[i >> 2] |= (ba[off + i] & 0xFF) << (24 - (i % 4) * 8);
  blks[i >> 2] |= 0x80 << (24 - (i % 4) * 8);
  blks[nblk * 16 - 1] = len * 8;
  return blks;
}

/*
 * Add integers, wrapping at 2^32. This uses 16-bit operations internally 
 * to work around bugs in some JS interpreters.
 */
function 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));
}

/*
 * Perform the appropriate triplet combination function for the current
 * iteration
 */
function 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 kt(t)
{
  return (t < 20) ?  1518500249 : (t < 40) ?  1859775393 :
         (t < 60) ? -1894007588 : -899497514;
}

/*
 * Take a string and return the hex representation of its SHA-1.
 */
function calcSHA1(str)
{
  return calcSHA1Blks(str2blks_SHA1(str));
}

function calcSHA1Hex(str)
{
  return calcSHA1Blks(hex2blks_SHA1(str));
}

function calcSHA1BA(ba)
{
  return calcSHA1Blks(ba2blks_SHA1(ba, 0, ba.length));
}

function calcSHA1BAEx(ba, off, len)
{
  return calcSHA1Blks(ba2blks_SHA1(ba, off, len));
}

function calcSHA1Blks(x)
{
  var s = calcSHA1Raw(x);
  return hex(s[0]) + hex(s[1]) + hex(s[2]) + hex(s[3]) + hex(s[4]);
}

function calcSHA1Raw(x)
{
  var w = new 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++)
    {
      var t;
      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);
      t = add(add(rol(a, 5), ft(j, b, c, d)), add(add(e, w[j]), kt(j)));
      e = d;
      d = c;
      c = rol(b, 30);
      b = a;
      a = t;
    }

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

function core_sha1(x, len) {
  x[len >> 5] |= 0x80 << (24 - len % 32);
  x[((len + 64 >> 9) << 4) + 15] = len;
  return calcSHA1Raw(x);
}


/* File: Templates/javascript/jsbn.js */
// Copyright (c) 2005  Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.

// Basic JavaScript BN library - subset useful for RSA encryption.

// Bits per digit
var dbits;

// JavaScript engine analysis
var canary = 0xdeadbeefcafe;
var j_lm = ((canary&0xffffff)==0xefcafe);

// (public) Constructor
function BigInteger(a,b,c) {
  if(a != null)
    if("number" == typeof a) this.fromNumber(a,b,c);
    else if(b == null && "string" != typeof a) this.fromString(a,256);
    else this.fromString(a,b);
}

// return new, unset BigInteger
function nbi() { return new BigInteger(null); }

// am: Compute w_j += (x*this_i), propagate carries,
// c is initial carry, returns final carry.
// c < 3*dvalue, x < 2*dvalue, this_i < dvalue
// We need to select the fastest one that works in this environment.

// am1: use a single mult and divide to get the high bits,
// max digit bits should be 26 because
// max internal value = 2*dvalue^2-2*dvalue (< 2^53)
function am1(i,x,w,j,c,n) {
  while(--n >= 0) {
    var v = x*this[i++]+w[j]+c;
    c = Math.floor(v/0x4000000);
    w[j++] = v&0x3ffffff;
  }
  return c;
}
// am2 avoids a big mult-and-extract completely.
// Max digit bits should be <= 30 because we do bitwise ops
// on values up to 2*hdvalue^2-hdvalue-1 (< 2^31)
function am2(i,x,w,j,c,n) {
  var xl = x&0x7fff, xh = x>>15;
  while(--n >= 0) {
    var l = this[i]&0x7fff;
    var h = this[i++]>>15;
    var m = xh*l+h*xl;
    l = xl*l+((m&0x7fff)<<15)+w[j]+(c&0x3fffffff);
    c = (l>>>30)+(m>>>15)+xh*h+(c>>>30);
    w[j++] = l&0x3fffffff;
  }
  return c;
}
// Alternately, set max digit bits to 28 since some
// browsers slow down when dealing with 32-bit numbers.
function am3(i,x,w,j,c,n) {
  var xl = x&0x3fff, xh = x>>14;
  while(--n >= 0) {
    var l = this[i]&0x3fff;
    var h = this[i++]>>14;
    var m = xh*l+h*xl;
    l = xl*l+((m&0x3fff)<<14)+w[j]+c;
    c = (l>>28)+(m>>14)+xh*h;
    w[j++] = l&0xfffffff;
  }
  return c;
}
if(j_lm && (navigator.appName == "Microsoft Internet Explorer")) {
  BigInteger.prototype.am = am2;
  dbits = 30;
}
else if(j_lm && (navigator.appName != "Netscape")) {
  BigInteger.prototype.am = am1;
  dbits = 26;
}
else { // Mozilla/Netscape seems to prefer am3
  BigInteger.prototype.am = am3;
  dbits = 28;
}

BigInteger.prototype.DB = dbits;
BigInteger.prototype.DM = ((1<<dbits)-1);
BigInteger.prototype.DV = (1<<dbits);

var BI_FP = 52;
BigInteger.prototype.FV = Math.pow(2,BI_FP);
BigInteger.prototype.F1 = BI_FP-dbits;
BigInteger.prototype.F2 = 2*dbits-BI_FP;

// Digit conversions
var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";
var BI_RC = new Array();
var rr,vv;
rr = "0".charCodeAt(0);
for(vv = 0; vv <= 9; ++vv) BI_RC[rr++] = vv;
rr = "a".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;
rr = "A".charCodeAt(0);
for(vv = 10; vv < 36; ++vv) BI_RC[rr++] = vv;

function int2char(n) { return BI_RM.charAt(n); }
function intAt(s,i) {
  var c = BI_RC[s.charCodeAt(i)];
  return (c==null)?-1:c;
}

// (protected) copy this to r
function bnpCopyTo(r) {
  for(var i = this.t-1; i >= 0; --i) r[i] = this[i];
  r.t = this.t;
  r.s = this.s;
}

// (protected) set from integer value x, -DV <= x < DV
function bnpFromInt(x) {
  this.t = 1;
  this.s = (x<0)?-1:0;
  if(x > 0) this[0] = x;
  else if(x < -1) this[0] = x+DV;
  else this.t = 0;
}

// return bigint initialized to value
function nbv(i) { var r = nbi(); r.fromInt(i); return r; }

// (protected) set from string and radix
function bnpFromString(s,b) {
  var k;
  if(b == 16) k = 4;
  else if(b == 8) k = 3;
  else if(b == 256) k = 8; // byte array
  else if(b == 2) k = 1;
  else if(b == 32) k = 5;
  else if(b == 4) k = 2;
  else { this.fromRadix(s,b); return; }
  this.t = 0;
  this.s = 0;
  var i = s.length, mi = false, sh = 0;
  while(--i >= 0) {
    var x = (k==8)?s[i]&0xff:intAt(s,i);
    if(x < 0) {
      if(s.charAt(i) == "-") mi = true;
      continue;
    }
    mi = false;
    if(sh == 0)
      this[this.t++] = x;
    else if(sh+k > this.DB) {
      this[this.t-1] |= (x&((1<<(this.DB-sh))-1))<<sh;
      this[this.t++] = (x>>(this.DB-sh));
    }
    else  
      this[this.t-1] |= x<<sh;
    sh += k;
    if(sh >= this.DB) sh -= this.DB;
  }
  if(k == 8 && (s[0]&0x80) != 0) {
    this.s = -1;
    if(sh > 0) this[this.t-1] |= ((1<<(this.DB-sh))-1)<<sh;
  }
  this.clamp();
  if(mi) BigInteger.ZERO.subTo(this,this);
}

// (protected) clamp off excess high words
function bnpClamp() {
  var c = this.s&this.DM;
  while(this.t > 0 && this[this.t-1] == c) --this.t;
}

// (public) return string representation in given radix
function bnToString(b) {
  if(this.s < 0) return "-"+this.negate().toString(b);
  var k;
  if(b == 16) k = 4;
  else if(b == 8) k = 3;
  else if(b == 2) k = 1;
  else if(b == 32) k = 5;
  else if(b == 4) k = 2;
  else return this.toRadix(b);
  var km = (1<<k)-1, d, m = false, r = "", i = this.t;
  var p = this.DB-(i*this.DB)%k;
  if(i-- > 0) {
    if(p < this.DB && (d = this[i]>>p) > 0) { m = true; r = int2char(d); }
    while(i >= 0) {
      if(p < k) {
        d = (this[i]&((1<<p)-1))<<(k-p);
        d |= this[--i]>>(p+=this.DB-k);
      }
      else {
        d = (this[i]>>(p-=k))&km;
        if(p <= 0) { p += this.DB; --i; }
      }
      if(d > 0) m = true;
      if(m) r += int2char(d);
    }
  }
  return m?r:"0";
}

// (public) -this
function bnNegate() { var r = nbi(); BigInteger.ZERO.subTo(this,r); return r; }

// (public) |this|
function bnAbs() { return (this.s<0)?this.negate():this; }

// (public) return + if this > a, - if this < a, 0 if equal
function bnCompareTo(a) {
  var r = this.s-a.s;
  if(r != 0) return r;
  var i = this.t;
  r = i-a.t;
  if(r != 0) return r;
  while(--i >= 0) if((r=this[i]-a[i]) != 0) return r;
  return 0;
}

// returns bit length of the integer x
function nbits(x) {
  var r = 1, t;
  if((t=x>>>16) != 0) { x = t; r += 16; }
  if((t=x>>8) != 0) { x = t; r += 8; }
  if((t=x>>4) != 0) { x = t; r += 4; }
  if((t=x>>2) != 0) { x = t; r += 2; }
  if((t=x>>1) != 0) { x = t; r += 1; }
  return r;
}

// (public) return the number of bits in "this"
function bnBitLength() {
  if(this.t <= 0) return 0;
  return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));
}

// (protected) r = this << n*DB
function bnpDLShiftTo(n,r) {
  var i;
  for(i = this.t-1; i >= 0; --i) r[i+n] = this[i];
  for(i = n-1; i >= 0; --i) r[i] = 0;
  r.t = this.t+n;
  r.s = this.s;
}

// (protected) r = this >> n*DB
function bnpDRShiftTo(n,r) {
  for(var i = n; i < this.t; ++i) r[i-n] = this[i];
  r.t = Math.max(this.t-n,0);
  r.s = this.s;
}

// (protected) r = this << n
function bnpLShiftTo(n,r) {
  var bs = n%this.DB;
  var cbs = this.DB-bs;
  var bm = (1<<cbs)-1;
  var ds = Math.floor(n/this.DB), c = (this.s<<bs)&this.DM, i;
  for(i = this.t-1; i >= 0; --i) {
    r[i+ds+1] = (this[i]>>cbs)|c;
    c = (this[i]&bm)<<bs;
  }
  for(i = ds-1; i >= 0; --i) r[i] = 0;
  r[ds] = c;
  r.t = this.t+ds+1;
  r.s = this.s;
  r.clamp();
}

// (protected) r = this >> n
function bnpRShiftTo(n,r) {
  r.s = this.s;
  var ds = Math.floor(n/this.DB);
  if(ds >= this.t) { r.t = 0; return; }
  var bs = n%this.DB;
  var cbs = this.DB-bs;
  var bm = (1<<bs)-1;
  r[0] = this[ds]>>bs;
  for(var i = ds+1; i < this.t; ++i) {
    r[i-ds-1] |= (this[i]&bm)<<cbs;
    r[i-ds] = this[i]>>bs;
  }
  if(bs > 0) r[this.t-ds-1] |= (this.s&bm)<<cbs;
  r.t = this.t-ds;
  r.clamp();
}

// (protected) r = this - a
function bnpSubTo(a,r) {
  var i = 0, c = 0, m = Math.min(a.t,this.t);
  while(i < m) {
    c += this[i]-a[i];
    r[i++] = c&this.DM;
    c >>= this.DB;
  }
  if(a.t < this.t) {
    c -= a.s;
    while(i < this.t) {
      c += this[i];
      r[i++] = c&this.DM;
      c >>= this.DB;
    }
    c += this.s;
  }
  else {
    c += this.s;
    while(i < a.t) {
      c -= a[i];
      r[i++] = c&this.DM;
      c >>= this.DB;
    }
    c -= a.s;
  }
  r.s = (c<0)?-1:0;
  if(c < -1) r[i++] = this.DV+c;
  else if(c > 0) r[i++] = c;
  r.t = i;
  r.clamp();
}

// (protected) r = this * a, r != this,a (HAC 14.12)
// "this" should be the larger one if appropriate.
function bnpMultiplyTo(a,r) {
  var x = this.abs(), y = a.abs();
  var i = x.t;
  r.t = i+y.t;
  while(--i >= 0) r[i] = 0;
  for(i = 0; i < y.t; ++i) r[i+x.t] = x.am(0,y[i],r,i,0,x.t);
  r.s = 0;
  r.clamp();
  if(this.s != a.s) BigInteger.ZERO.subTo(r,r);
}

// (protected) r = this^2, r != this (HAC 14.16)
function bnpSquareTo(r) {
  var x = this.abs();
  var i = r.t = 2*x.t;
  while(--i >= 0) r[i] = 0;
  for(i = 0; i < x.t-1; ++i) {
    var c = x.am(i,x[i],r,2*i,0,1);
    if((r[i+x.t]+=x.am(i+1,2*x[i],r,2*i+1,c,x.t-i-1)) >= x.DV) {
      r[i+x.t] -= x.DV;
      r[i+x.t+1] = 1;
    }
  }
  if(r.t > 0) r[r.t-1] += x.am(i,x[i],r,2*i,0,1);
  r.s = 0;
  r.clamp();
}

// (protected) divide this by m, quotient and remainder to q, r (HAC 14.20)
// r != q, this != m.  q or r may be null.
function bnpDivRemTo(m,q,r) {
  var pm = m.abs();
  if(pm.t <= 0) return;
  var pt = this.abs();
  if(pt.t < pm.t) {
    if(q != null) q.fromInt(0);
    if(r != null) this.copyTo(r);
    return;
  }
  if(r == null) r = nbi();
  var y = nbi(), ts = this.s, ms = m.s;
  var nsh = this.DB-nbits(pm[pm.t-1]);	// normalize modulus
  if(nsh > 0) { pm.lShiftTo(nsh,y); pt.lShiftTo(nsh,r); }
  else { pm.copyTo(y); pt.copyTo(r); }
  var ys = y.t;
  var y0 = y[ys-1];
  if(y0 == 0) return;
  var yt = y0*(1<<this.F1)+((ys>1)?y[ys-2]>>this.F2:0);
  var d1 = this.FV/yt, d2 = (1<<this.F1)/yt, e = 1<<this.F2;
  var i = r.t, j = i-ys, t = (q==null)?nbi():q;
  y.dlShiftTo(j,t);
  if(r.compareTo(t) >= 0) {
    r[r.t++] = 1;
    r.subTo(t,r);
  }
  BigInteger.ONE.dlShiftTo(ys,t);
  t.subTo(y,y);	// "negative" y so we can replace sub with am later
  while(y.t < ys) y[y.t++] = 0;
  while(--j >= 0) {
    // Estimate quotient digit
    var qd = (r[--i]==y0)?this.DM:Math.floor(r[i]*d1+(r[i-1]+e)*d2);
    if((r[i]+=y.am(0,qd,r,j,0,ys)) < qd) {	// Try it out
      y.dlShiftTo(j,t);
      r.subTo(t,r);
      while(r[i] < --qd) r.subTo(t,r);
    }
  }
  if(q != null) {
    r.drShiftTo(ys,q);
    if(ts != ms) BigInteger.ZERO.subTo(q,q);
  }
  r.t = ys;
  r.clamp();
  if(nsh > 0) r.rShiftTo(nsh,r);	// Denormalize remainder
  if(ts < 0) BigInteger.ZERO.subTo(r,r);
}

// (public) this mod a
function bnMod(a) {
  var r = nbi();
  this.abs().divRemTo(a,null,r);
  if(this.s < 0 && r.compareTo(BigInteger.ZERO) > 0) a.subTo(r,r);
  return r;
}

// Modular reduction using "classic" algorithm
function Classic(m) { this.m = m; }
function cConvert(x) {
  if(x.s < 0 || x.compareTo(this.m) >= 0) return x.mod(this.m);
  else return x;
}
function cRevert(x) { return x; }
function cReduce(x) { x.divRemTo(this.m,null,x); }
function cMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }
function cSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

Classic.prototype.convert = cConvert;
Classic.prototype.revert = cRevert;
Classic.prototype.reduce = cReduce;
Classic.prototype.mulTo = cMulTo;
Classic.prototype.sqrTo = cSqrTo;

// (protected) return "-1/this % 2^DB"; useful for Mont. reduction
// justification:
//         xy == 1 (mod m)
//         xy =  1+km
//   xy(2-xy) = (1+km)(1-km)
// x[y(2-xy)] = 1-k^2m^2
// x[y(2-xy)] == 1 (mod m^2)
// if y is 1/x mod m, then y(2-xy) is 1/x mod m^2
// should reduce x and y(2-xy) by m^2 at each step to keep size bounded.
// JS multiply "overflows" differently from C/C++, so care is needed here.
function bnpInvDigit() {
  if(this.t < 1) return 0;
  var x = this[0];
  if((x&1) == 0) return 0;
  var y = x&3;		// y == 1/x mod 2^2
  y = (y*(2-(x&0xf)*y))&0xf;	// y == 1/x mod 2^4
  y = (y*(2-(x&0xff)*y))&0xff;	// y == 1/x mod 2^8
  y = (y*(2-(((x&0xffff)*y)&0xffff)))&0xffff;	// y == 1/x mod 2^16
  // last step - calculate inverse mod DV directly;
  // assumes 16 < DB <= 32 and assumes ability to handle 48-bit ints
  y = (y*(2-x*y%this.DV))%this.DV;		// y == 1/x mod 2^dbits
  // we really want the negative inverse, and -DV < y < DV
  return (y>0)?this.DV-y:-y;
}

// Montgomery reduction
function Montgomery(m) {
  this.m = m;
  this.mp = m.invDigit();
  this.mpl = this.mp&0x7fff;
  this.mph = this.mp>>15;
  this.um = (1<<(m.DB-15))-1;
  this.mt2 = 2*m.t;
}

// xR mod m
function montConvert(x) {
  var r = nbi();
  x.abs().dlShiftTo(this.m.t,r);
  r.divRemTo(this.m,null,r);
  if(x.s < 0 && r.compareTo(BigInteger.ZERO) > 0) this.m.subTo(r,r);
  return r;
}

// x/R mod m
function montRevert(x) {
  var r = nbi();
  x.copyTo(r);
  this.reduce(r);
  return r;
}

// x = x/R mod m (HAC 14.32)
function montReduce(x) {
  while(x.t <= this.mt2)	// pad x so am has enough room later
    x[x.t++] = 0;
  for(var i = 0; i < this.m.t; ++i) {
    // faster way of calculating u0 = x[i]*mp mod DV
    var j = x[i]&0x7fff;
    var u0 = (j*this.mpl+(((j*this.mph+(x[i]>>15)*this.mpl)&this.um)<<15))&x.DM;
    // use am to combine the multiply-shift-add into one call
    j = i+this.m.t;
    x[j] += this.m.am(0,u0,x,i,0,this.m.t);
    // propagate carry
    while(x[j] >= x.DV) { x[j] -= x.DV; x[++j]++; }
  }
  x.clamp();
  x.drShiftTo(this.m.t,x);
  if(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

// r = "x^2/R mod m"; x != r
function montSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

// r = "xy/R mod m"; x,y != r
function montMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }

Montgomery.prototype.convert = montConvert;
Montgomery.prototype.revert = montRevert;
Montgomery.prototype.reduce = montReduce;
Montgomery.prototype.mulTo = montMulTo;
Montgomery.prototype.sqrTo = montSqrTo;

// (protected) true iff this is even
function bnpIsEven() { return ((this.t>0)?(this[0]&1):this.s) == 0; }

// (protected) this^e, e < 2^32, doing sqr and mul with "r" (HAC 14.79)
function bnpExp(e,z) {
  if(e > 0xffffffff || e < 1) return BigInteger.ONE;
  var r = nbi(), r2 = nbi(), g = z.convert(this), i = nbits(e)-1;
  g.copyTo(r);
  while(--i >= 0) {
    z.sqrTo(r,r2);
    if((e&(1<<i)) > 0) z.mulTo(r2,g,r);
    else { var t = r; r = r2; r2 = t; }
  }
  return z.revert(r);
}

// (public) this^e % m, 0 <= e < 2^32
function bnModPowInt(e,m) {
  var z;
  if(e < 256 || m.isEven()) z = new Classic(m); else z = new Montgomery(m);
  return this.exp(e,z);
}

// protected
BigInteger.prototype.copyTo = bnpCopyTo;
BigInteger.prototype.fromInt = bnpFromInt;
BigInteger.prototype.fromString = bnpFromString;
BigInteger.prototype.clamp = bnpClamp;
BigInteger.prototype.dlShiftTo = bnpDLShiftTo;
BigInteger.prototype.drShiftTo = bnpDRShiftTo;
BigInteger.prototype.lShiftTo = bnpLShiftTo;
BigInteger.prototype.rShiftTo = bnpRShiftTo;
BigInteger.prototype.subTo = bnpSubTo;
BigInteger.prototype.multiplyTo = bnpMultiplyTo;
BigInteger.prototype.squareTo = bnpSquareTo;
BigInteger.prototype.divRemTo = bnpDivRemTo;
BigInteger.prototype.invDigit = bnpInvDigit;
BigInteger.prototype.isEven = bnpIsEven;
BigInteger.prototype.exp = bnpExp;

// public
BigInteger.prototype.toString = bnToString;
BigInteger.prototype.negate = bnNegate;
BigInteger.prototype.abs = bnAbs;
BigInteger.prototype.compareTo = bnCompareTo;
BigInteger.prototype.bitLength = bnBitLength;
BigInteger.prototype.mod = bnMod;
BigInteger.prototype.modPowInt = bnModPowInt;

// "constants"
BigInteger.ZERO = nbv(0);
BigInteger.ONE = nbv(1);


/* File: Templates/javascript/jsbn2.js */
// Copyright (c) 2005  Tom Wu
// All Rights Reserved.
// See "LICENSE" for details.

// Extended JavaScript BN functions, required for RSA private ops.

// (public)
function bnClone() { var r = nbi(); this.copyTo(r); return r; }

// (public) return value as integer
function bnIntValue() {
  if(this.s < 0) {
    if(this.t == 1) return this[0]-this.DV;
    else if(this.t == 0) return -1;
  }
  else if(this.t == 1) return this[0];
  else if(this.t == 0) return 0;
  // assumes 16 < DB < 32
  return ((this[1]&((1<<(32-this.DB))-1))<<this.DB)|this[0];
}

// (public) return value as byte
function bnByteValue() { return (this.t==0)?this.s:(this[0]<<24)>>24; }

// (public) return value as short (assumes DB>=16)
function bnShortValue() { return (this.t==0)?this.s:(this[0]<<16)>>16; }

// (protected) return x s.t. r^x < DV
function bnpChunkSize(r) { return Math.floor(Math.LN2*this.DB/Math.log(r)); }

// (public) 0 if this == 0, 1 if this > 0
function bnSigNum() {
  if(this.s < 0) return -1;
  else if(this.t <= 0 || (this.t == 1 && this[0] <= 0)) return 0;
  else return 1;
}

// (protected) convert to radix string
function bnpToRadix(b) {
  if(b == null) b = 10;
  if(this.signum() == 0 || b < 2 || b > 36) return "0";
  var cs = this.chunkSize(b);
  var a = Math.pow(b,cs);
  var d = nbv(a), y = nbi(), z = nbi(), r = "";
  this.divRemTo(d,y,z);
  while(y.signum() > 0) {
    r = (a+z.intValue()).toString(b).substr(1) + r;
    y.divRemTo(d,y,z);
  }
  return z.intValue().toString(b) + r;
}

// (protected) convert from radix string
function bnpFromRadix(s,b) {
  this.fromInt(0);
  if(b == null) b = 10;
  var cs = this.chunkSize(b);
  var d = Math.pow(b,cs), mi = false, j = 0, w = 0;
  for(var i = 0; i < s.length; ++i) {
    var x = intAt(s,i);
    if(x < 0) {
      if(s.charAt(i) == "-" && this.signum() == 0) mi = true;
      continue;
    }
    w = b*w+x;
    if(++j >= cs) {
      this.dMultiply(d);
      this.dAddOffset(w,0);
      j = 0;
      w = 0;
    }
  }
  if(j > 0) {
    this.dMultiply(Math.pow(b,j));
    this.dAddOffset(w,0);
  }
  if(mi) BigInteger.ZERO.subTo(this,this);
}

// (protected) alternate constructor
function bnpFromNumber(a,b,c) {
  if("number" == typeof b) {
    // new BigInteger(int,int,RNG)
    if(a < 2) this.fromInt(1);
    else {
      this.fromNumber(a,c);
      if(!this.testBit(a-1))	// force MSB set
        this.bitwiseTo(BigInteger.ONE.shiftLeft(a-1),op_or,this);
      if(this.isEven()) this.dAddOffset(1,0); // force odd
      while(!this.isProbablePrime(b)) {
        this.dAddOffset(2,0);
        if(this.bitLength() > a) this.subTo(BigInteger.ONE.shiftLeft(a-1),this);
      }
    }
  }
  else {
    // new BigInteger(int,RNG)
    var x = new Array(), t = a&7;
    x.length = (a>>3)+1;
    b.nextBytes(x);
    if(t > 0) x[0] &= ((1<<t)-1); else x[0] = 0;
    this.fromString(x,256);
  }
}

// (public) convert to bigendian byte array
function bnToByteArray() {
  var i = this.t, r = new Array();
  r[0] = this.s;
  var p = this.DB-(i*this.DB)%8, d, k = 0;
  if(i-- > 0) {
    if(p < this.DB && (d = this[i]>>p) != (this.s&this.DM)>>p)
      r[k++] = d|(this.s<<(this.DB-p));
    while(i >= 0) {
      if(p < 8) {
        d = (this[i]&((1<<p)-1))<<(8-p);
        d |= this[--i]>>(p+=this.DB-8);
      }
      else {
        d = (this[i]>>(p-=8))&0xff;
        if(p <= 0) { p += this.DB; --i; }
      }
      if((d&0x80) != 0) d |= -256;
      if(k == 0 && (this.s&0x80) != (d&0x80)) ++k;
      if(k > 0 || d != this.s) r[k++] = d;
    }
  }
  return r;
}

function bnEquals(a) { return(this.compareTo(a)==0); }
function bnMin(a) { return(this.compareTo(a)<0)?this:a; }
function bnMax(a) { return(this.compareTo(a)>0)?this:a; }

// (protected) r = this op a (bitwise)
function bnpBitwiseTo(a,op,r) {
  var i, f, m = Math.min(a.t,this.t);
  for(i = 0; i < m; ++i) r[i] = op(this[i],a[i]);
  if(a.t < this.t) {
    f = a.s&this.DM;
    for(i = m; i < this.t; ++i) r[i] = op(this[i],f);
    r.t = this.t;
  }
  else {
    f = this.s&this.DM;
    for(i = m; i < a.t; ++i) r[i] = op(f,a[i]);
    r.t = a.t;
  }
  r.s = op(this.s,a.s);
  r.clamp();
}

// (public) this & a
function op_and(x,y) { return x&y; }
function bnAnd(a) { var r = nbi(); this.bitwiseTo(a,op_and,r); return r; }

// (public) this | a
function op_or(x,y) { return x|y; }
function bnOr(a) { var r = nbi(); this.bitwiseTo(a,op_or,r); return r; }

// (public) this ^ a
function op_xor(x,y) { return x^y; }
function bnXor(a) { var r = nbi(); this.bitwiseTo(a,op_xor,r); return r; }

// (public) this & ~a
function op_andnot(x,y) { return x&~y; }
function bnAndNot(a) { var r = nbi(); this.bitwiseTo(a,op_andnot,r); return r; }

// (public) ~this
function bnNot() {
  var r = nbi();
  for(var i = 0; i < this.t; ++i) r[i] = this.DM&~this[i];
  r.t = this.t;
  r.s = ~this.s;
  return r;
}

// (public) this << n
function bnShiftLeft(n) {
  var r = nbi();
  if(n < 0) this.rShiftTo(-n,r); else this.lShiftTo(n,r);
  return r;
}

// (public) this >> n
function bnShiftRight(n) {
  var r = nbi();
  if(n < 0) this.lShiftTo(-n,r); else this.rShiftTo(n,r);
  return r;
}

// return index of lowest 1-bit in x, x < 2^31
function lbit(x) {
  if(x == 0) return -1;
  var r = 0;
  if((x&0xffff) == 0) { x >>= 16; r += 16; }
  if((x&0xff) == 0) { x >>= 8; r += 8; }
  if((x&0xf) == 0) { x >>= 4; r += 4; }
  if((x&3) == 0) { x >>= 2; r += 2; }
  if((x&1) == 0) ++r;
  return r;
}

// (public) returns index of lowest 1-bit (or -1 if none)
function bnGetLowestSetBit() {
  for(var i = 0; i < this.t; ++i)
    if(this[i] != 0) return i*this.DB+lbit(this[i]);
  if(this.s < 0) return this.t*this.DB;
  return -1;
}

// return number of 1 bits in x
function cbit(x) {
  var r = 0;
  while(x != 0) { x &= x-1; ++r; }
  return r;
}

// (public) return number of set bits
function bnBitCount() {
  var r = 0, x = this.s&this.DM;
  for(var i = 0; i < this.t; ++i) r += cbit(this[i]^x);
  return r;
}

// (public) true iff nth bit is set
function bnTestBit(n) {
  var j = Math.floor(n/this.DB);
  if(j >= this.t) return(this.s!=0);
  return((this[j]&(1<<(n%this.DB)))!=0);
}

// (protected) this op (1<<n)
function bnpChangeBit(n,op) {
  var r = BigInteger.ONE.shiftLeft(n);
  this.bitwiseTo(r,op,r);
  return r;
}

// (public) this | (1<<n)
function bnSetBit(n) { return this.changeBit(n,op_or); }

// (public) this & ~(1<<n)
function bnClearBit(n) { return this.changeBit(n,op_andnot); }

// (public) this ^ (1<<n)
function bnFlipBit(n) { return this.changeBit(n,op_xor); }

// (protected) r = this + a
function bnpAddTo(a,r) {
  var i = 0, c = 0, m = Math.min(a.t,this.t);
  while(i < m) {
    c += this[i]+a[i];
    r[i++] = c&this.DM;
    c >>= this.DB;
  }
  if(a.t < this.t) {
    c += a.s;
    while(i < this.t) {
      c += this[i];
      r[i++] = c&this.DM;
      c >>= this.DB;
    }
    c += this.s;
  }
  else {
    c += this.s;
    while(i < a.t) {
      c += a[i];
      r[i++] = c&this.DM;
      c >>= this.DB;
    }
    c += a.s;
  }
  r.s = (c<0)?-1:0;
  if(c > 0) r[i++] = c;
  else if(c < -1) r[i++] = this.DV+c;
  r.t = i;
  r.clamp();
}

// (public) this + a
function bnAdd(a) { var r = nbi(); this.addTo(a,r); return r; }

// (public) this - a
function bnSubtract(a) { var r = nbi(); this.subTo(a,r); return r; }

// (public) this * a
function bnMultiply(a) { var r = nbi(); this.multiplyTo(a,r); return r; }

// (public) this / a
function bnDivide(a) { var r = nbi(); this.divRemTo(a,r,null); return r; }

// (public) this % a
function bnRemainder(a) { var r = nbi(); this.divRemTo(a,null,r); return r; }

// (public) [this/a,this%a]
function bnDivideAndRemainder(a) {
  var q = nbi(), r = nbi();
  this.divRemTo(a,q,r);
  return new Array(q,r);
}

// (protected) this *= n, this >= 0, 1 < n < DV
function bnpDMultiply(n) {
  this[this.t] = this.am(0,n-1,this,0,0,this.t);
  ++this.t;
  this.clamp();
}

// (protected) this += n << w words, this >= 0
function bnpDAddOffset(n,w) {
  while(this.t <= w) this[this.t++] = 0;
  this[w] += n;
  while(this[w] >= this.DV) {
    this[w] -= this.DV;
    if(++w >= this.t) this[this.t++] = 0;
    ++this[w];
  }
}

// A "null" reducer
function NullExp() {}
function nNop(x) { return x; }
function nMulTo(x,y,r) { x.multiplyTo(y,r); }
function nSqrTo(x,r) { x.squareTo(r); }

NullExp.prototype.convert = nNop;
NullExp.prototype.revert = nNop;
NullExp.prototype.mulTo = nMulTo;
NullExp.prototype.sqrTo = nSqrTo;

// (public) this^e
function bnPow(e) { return this.exp(e,new NullExp()); }

// (protected) r = lower n words of "this * a", a.t <= n
// "this" should be the larger one if appropriate.
function bnpMultiplyLowerTo(a,n,r) {
  var i = Math.min(this.t+a.t,n);
  r.s = 0; // assumes a,this >= 0
  r.t = i;
  while(i > 0) r[--i] = 0;
  var j;
  for(j = r.t-this.t; i < j; ++i) r[i+this.t] = this.am(0,a[i],r,i,0,this.t);
  for(j = Math.min(a.t,n); i < j; ++i) this.am(0,a[i],r,i,0,n-i);
  r.clamp();
}

// (protected) r = "this * a" without lower n words, n > 0
// "this" should be the larger one if appropriate.
function bnpMultiplyUpperTo(a,n,r) {
  --n;
  var i = r.t = this.t+a.t-n;
  r.s = 0; // assumes a,this >= 0
  while(--i >= 0) r[i] = 0;
  for(i = Math.max(n-this.t,0); i < a.t; ++i)
    r[this.t+i-n] = this.am(n-i,a[i],r,0,0,this.t+i-n);
  r.clamp();
  r.drShiftTo(1,r);
}

// Barrett modular reduction
function Barrett(m) {
  // setup Barrett
  this.r2 = nbi();
  this.q3 = nbi();
  BigInteger.ONE.dlShiftTo(2*m.t,this.r2);
  this.mu = this.r2.divide(m);
  this.m = m;
}

function barrettConvert(x) {
  if(x.s < 0 || x.t > 2*this.m.t) return x.mod(this.m);
  else if(x.compareTo(this.m) < 0) return x;
  else { var r = nbi(); x.copyTo(r); this.reduce(r); return r; }
}

function barrettRevert(x) { return x; }

// x = x mod m (HAC 14.42)
function barrettReduce(x) {
  x.drShiftTo(this.m.t-1,this.r2);
  if(x.t > this.m.t+1) { x.t = this.m.t+1; x.clamp(); }
  this.mu.multiplyUpperTo(this.r2,this.m.t+1,this.q3);
  this.m.multiplyLowerTo(this.q3,this.m.t+1,this.r2);
  while(x.compareTo(this.r2) < 0) x.dAddOffset(1,this.m.t+1);
  x.subTo(this.r2,x);
  while(x.compareTo(this.m) >= 0) x.subTo(this.m,x);
}

// r = x^2 mod m; x != r
function barrettSqrTo(x,r) { x.squareTo(r); this.reduce(r); }

// r = x*y mod m; x,y != r
function barrettMulTo(x,y,r) { x.multiplyTo(y,r); this.reduce(r); }

Barrett.prototype.convert = barrettConvert;
Barrett.prototype.revert = barrettRevert;
Barrett.prototype.reduce = barrettReduce;
Barrett.prototype.mulTo = barrettMulTo;
Barrett.prototype.sqrTo = barrettSqrTo;

// (public) this^e % m (HAC 14.85)
function bnModPow(e,m) {
  var i = e.bitLength(), k, r = nbv(1), z;
  if(i <= 0) return r;
  else if(i < 18) k = 1;
  else if(i < 48) k = 3;
  else if(i < 144) k = 4;
  else if(i < 768) k = 5;
  else k = 6;
  if(i < 8)
    z = new Classic(m);
  else if(m.isEven())
    z = new Barrett(m);
  else
    z = new Montgomery(m);

  // precomputation
  var g = new Array(), n = 3, k1 = k-1, km = (1<<k)-1;
  g[1] = z.convert(this);
  if(k > 1) {
    var g2 = nbi();
    z.sqrTo(g[1],g2);
    while(n <= km) {
      g[n] = nbi();
      z.mulTo(g2,g[n-2],g[n]);
      n += 2;
    }
  }

  var j = e.t-1, w, is1 = true, r2 = nbi(), t;
  i = nbits(e[j])-1;
  while(j >= 0) {
    if(i >= k1) w = (e[j]>>(i-k1))&km;
    else {
      w = (e[j]&((1<<(i+1))-1))<<(k1-i);
      if(j > 0) w |= e[j-1]>>(this.DB+i-k1);
    }

    n = k;
    while((w&1) == 0) { w >>= 1; --n; }
    if((i -= n) < 0) { i += this.DB; --j; }
    if(is1) {	// ret == 1, don't bother squaring or multiplying it
      g[w].copyTo(r);
      is1 = false;
    }
    else {
      while(n > 1) { z.sqrTo(r,r2); z.sqrTo(r2,r); n -= 2; }
      if(n > 0) z.sqrTo(r,r2); else { t = r; r = r2; r2 = t; }
      z.mulTo(r2,g[w],r);
    }

    while(j >= 0 && (e[j]&(1<<i)) == 0) {
      z.sqrTo(r,r2); t = r; r = r2; r2 = t;
      if(--i < 0) { i = this.DB-1; --j; }
    }
  }
  return z.revert(r);
}

// (public) gcd(this,a) (HAC 14.54)
function bnGCD(a) {
  var x = (this.s<0)?this.negate():this.clone();
  var y = (a.s<0)?a.negate():a.clone();
  if(x.compareTo(y) < 0) { var t = x; x = y; y = t; }
  var i = x.getLowestSetBit(), g = y.getLowestSetBit();
  if(g < 0) return x;
  if(i < g) g = i;
  if(g > 0) {
    x.rShiftTo(g,x);
    y.rShiftTo(g,y);
  }
  while(x.signum() > 0) {
    if((i = x.getLowestSetBit()) > 0) x.rShiftTo(i,x);
    if((i = y.getLowestSetBit()) > 0) y.rShiftTo(i,y);
    if(x.compareTo(y) >= 0) {
      x.subTo(y,x);
      x.rShiftTo(1,x);
    }
    else {
      y.subTo(x,y);
      y.rShiftTo(1,y);
    }
  }
  if(g > 0) y.lShiftTo(g,y);
  return y;
}

// (protected) this % n, n < 2^26
function bnpModInt(n) {
  if(n <= 0) return 0;
  var d = this.DV%n, r = (this.s<0)?n-1:0;
  if(this.t > 0)
    if(d == 0) r = this[0]%n;
    else for(var i = this.t-1; i >= 0; --i) r = (d*r+this[i])%n;
  return r;
}

// (public) 1/this % m (HAC 14.61)
function bnModInverse(m) {
  var ac = m.isEven();
  if((this.isEven() && ac) || m.signum() == 0) return BigInteger.ZERO;
  var u = m.clone(), v = this.clone();
  var a = nbv(1), b = nbv(0), c = nbv(0), d = nbv(1);
  while(u.signum() != 0) {
    while(u.isEven()) {
      u.rShiftTo(1,u);
      if(ac) {
        if(!a.isEven() || !b.isEven()) { a.addTo(this,a); b.subTo(m,b); }
        a.rShiftTo(1,a);
      }
      else if(!b.isEven()) b.subTo(m,b);
      b.rShiftTo(1,b);
    }
    while(v.isEven()) {
      v.rShiftTo(1,v);
      if(ac) {
        if(!c.isEven() || !d.isEven()) { c.addTo(this,c); d.subTo(m,d); }
        c.rShiftTo(1,c);
      }
      else if(!d.isEven()) d.subTo(m,d);
      d.rShiftTo(1,d);
    }
    if(u.compareTo(v) >= 0) {
      u.subTo(v,u);
      if(ac) a.subTo(c,a);
      b.subTo(d,b);
    }
    else {
      v.subTo(u,v);
      if(ac) c.subTo(a,c);
      d.subTo(b,d);
    }
  }
  if(v.compareTo(BigInteger.ONE) != 0) return BigInteger.ZERO;
  if(d.compareTo(m) >= 0) return d.subtract(m);
  if(d.signum() < 0) d.addTo(m,d); else return d;
  if(d.signum() < 0) return d.add(m); else return d;
}

var lowprimes = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61,67,71,73,79,83,89,97,101,103,107,109,113,127,131,137,139,149,151,157,163,167,173,179,181,191,193,197,199,211,223,227,229,233,239,241,251,257,263,269,271,277,281,283,293,307,311,313,317,331,337,347,349,353,359,367,373,379,383,389,397,401,409,419,421,431,433,439,443,449,457,461,463,467,479,487,491,499,503,509];
var lplim = (1<<26)/lowprimes[lowprimes.length-1];

// (public) test primality with certainty >= 1-.5^t
function bnIsProbablePrime(t) {
  var i, x = this.abs();
  if(x.t == 1 && x[0] <= lowprimes[lowprimes.length-1]) {
    for(i = 0; i < lowprimes.length; ++i)
      if(x[0] == lowprimes[i]) return true;
    return false;
  }
  if(x.isEven()) return false;
  i = 1;
  while(i < lowprimes.length) {
    var m = lowprimes[i], j = i+1;
    while(j < lowprimes.length && m < lplim) m *= lowprimes[j++];
    m = x.modInt(m);
    while(i < j) if(m%lowprimes[i++] == 0) return false;
  }
  return x.millerRabin(t);
}

// (protected) true if probably prime (HAC 4.24, Miller-Rabin)
function bnpMillerRabin(t) {
  var n1 = this.subtract(BigInteger.ONE);
  var k = n1.getLowestSetBit();
  if(k <= 0) return false;
  var r = n1.shiftRight(k);
  t = (t+1)>>1;
  if(t > lowprimes.length) t = lowprimes.length;
  var a = nbi();
  for(var i = 0; i < t; ++i) {
    a.fromInt(lowprimes[i]);
    var y = a.modPow(r,this);
    if(y.compareTo(BigInteger.ONE) != 0 && y.compareTo(n1) != 0) {
      var j = 1;
      while(j++ < k && y.compareTo(n1) != 0) {
        y = y.modPowInt(2,this);
        if(y.compareTo(BigInteger.ONE) == 0) return false;
      }
      if(y.compareTo(n1) != 0) return false;
    }
  }
  return true;
}

// protected
BigInteger.prototype.chunkSize = bnpChunkSize;
BigInteger.prototype.toRadix = bnpToRadix;
BigInteger.prototype.fromRadix = bnpFromRadix;
BigInteger.prototype.fromNumber = bnpFromNumber;
BigInteger.prototype.bitwiseTo = bnpBitwiseTo;
BigInteger.prototype.changeBit = bnpChangeBit;
BigInteger.prototype.addTo = bnpAddTo;
BigInteger.prototype.dMultiply = bnpDMultiply;
BigInteger.prototype.dAddOffset = bnpDAddOffset;
BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;
BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;
BigInteger.prototype.modInt = bnpModInt;
BigInteger.prototype.millerRabin = bnpMillerRabin;

// public
BigInteger.prototype.clone = bnClone;
BigInteger.prototype.intValue = bnIntValue;
BigInteger.prototype.byteValue = bnByteValue;
BigInteger.prototype.shortValue = bnShortValue;
BigInteger.prototype.signum = bnSigNum;
BigInteger.prototype.toByteArray = bnToByteArray;
BigInteger.prototype.equals = bnEquals;
BigInteger.prototype.min = bnMin;
BigInteger.prototype.max = bnMax;
BigInteger.prototype.and = bnAnd;
BigInteger.prototype.or = bnOr;
BigInteger.prototype.xor = bnXor;
BigInteger.prototype.andNot = bnAndNot;
BigInteger.prototype.not = bnNot;
BigInteger.prototype.shiftLeft = bnShiftLeft;
BigInteger.prototype.shiftRight = bnShiftRight;
BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;
BigInteger.prototype.bitCount = bnBitCount;
BigInteger.prototype.testBit = bnTestBit;
BigInteger.prototype.setBit = bnSetBit;
BigInteger.prototype.clearBit = bnClearBit;
BigInteger.prototype.flipBit = bnFlipBit;
BigInteger.prototype.add = bnAdd;
BigInteger.prototype.subtract = bnSubtract;
BigInteger.prototype.multiply = bnMultiply;
BigInteger.prototype.divide = bnDivide;
BigInteger.prototype.remainder = bnRemainder;
BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;
BigInteger.prototype.modPow = bnModPow;
BigInteger.prototype.modInverse = bnModInverse;
BigInteger.prototype.pow = bnPow;
BigInteger.prototype.gcd = bnGCD;
BigInteger.prototype.isProbablePrime = bnIsProbablePrime;

// BigInteger interfaces not implemented in jsbn:

// BigInteger(int signum, byte[] magnitude)
// double doubleValue()
// float floatValue()
// int hashCode()
// long longValue()
// static BigInteger valueOf(long val)


/* File: Templates/javascript/prng4.js */
/*prng4.js - uses Arcfour as a PRNG*/

 
function Arcfour() {
  this.i = 0;
  this.j = 0;
  this.S = new Array();
}

// Initialize arcfour context from key, an array of ints, each from [0..255]
function ARC4init(key) {
  var i, j, t;
  for(i = 0; i < 256; ++i)
    this.S[i] = i;
  j = 0;
  for(i = 0; i < 256; ++i) {
    j = (j + this.S[i] + key[i % key.length]) & 255;
    t = this.S[i];
    this.S[i] = this.S[j];
    this.S[j] = t;
  }
  this.i = 0;
  this.j = 0;
}

function ARC4next() {
  var t;
  this.i = (this.i + 1) & 255;
  this.j = (this.j + this.S[this.i]) & 255;
  t = this.S[this.i];
  this.S[this.i] = this.S[this.j];
  this.S[this.j] = t;
  return this.S[(t + this.S[this.i]) & 255];
}

Arcfour.prototype.init = ARC4init;
Arcfour.prototype.next = ARC4next;

// Plug in your RNG constructor here
function prng_newstate() {
  return new Arcfour();
}

// Pool size must be a multiple of 4 and greater than 32.
// An array of bytes the size of the pool will be passed to init()
var rng_psize = 256;


/* File: Templates/javascript/rng.js */
// Random number generator - requires a PRNG backend, e.g. prng4.js

// For best results, put code like
// <body onClick='rng_seed_time();' onKeyPress='rng_seed_time();'>
// in your main HTML document.


var rng_state;
var rng_pool;
var rng_pptr;

// Mix in a 32-bit integer into the pool
function rng_seed_int(x) {
  rng_pool[rng_pptr++] ^= x & 255;
  rng_pool[rng_pptr++] ^= (x >> 8) & 255;
  rng_pool[rng_pptr++] ^= (x >> 16) & 255;
  rng_pool[rng_pptr++] ^= (x >> 24) & 255;
  if(rng_pptr >= rng_psize) rng_pptr -= rng_psize;
  //alert("rng_psize: "+rng_psize);
}

// Mix in the current time (w/milliseconds) into the pool
function rng_seed_time() {
  rng_seed_int(new Date().getTime());
}

// Initialize the pool with junk if needed.
if(rng_pool == null) {
  
  rng_pool = new Array();
  rng_pptr = 0;
 
  var t;
  if(navigator.appName == "Netscape" && navigator.appVersion < "5" && window.crypto) {
    // Extract entropy (256 bits) from NS4 RNG if available
    var z = window.crypto.random(32);
    for(t = 0; t < z.length; ++t)
      rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
  }  
  while(rng_pptr < rng_psize) {  // extract some randomness from Math.random()
    t = Math.floor(65536 * Math.random());
    rng_pool[rng_pptr++] = t >>> 8;
    rng_pool[rng_pptr++] = t & 255;
  }
  rng_pptr = 0;
  rng_seed_time();
  //rng_seed_int(window.screenX);
  //rng_seed_int(window.screenY);
}

function rng_get_byte() {
  if(rng_state == null) {
    rng_seed_time();
    rng_state = prng_newstate();
    rng_state.init(rng_pool);
    for(rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr)
      rng_pool[rng_pptr] = 0;
    rng_pptr = 0;
    //rng_pool = null;
  }
  // TODO: allow reseeding after first request
  return rng_state.next();
}

function rng_get_bytes(ba) {
  var i;
  for(i = 0; i < ba.length; ++i) ba[i] = rng_get_byte();
}

function SecureRandom() {}

SecureRandom.prototype.nextBytes = rng_get_bytes;


/* File: Templates/javascript/myxyz.js */
var url       = null;       //xyz servlet
var app_name    = '';
var initialized   = 0;
var debug       = false;  //don't submit login just show if it works or not
var XYZ         = true;   //use XYZ or fail to old authentication
var submit_form   = false;
var acct_id     = null;
var user_name     = null;
var complete_authentication = false;
var complete_confirm_token = false;
var is_two_factor_user = false;
//pteng used to identify the user as second factor. used by WT to redirect to login on disconnect
var twoFactorUser = false;
var suppLongPwd = false;
var userSfTypes = [];
var selectedSF =null;

//default to 512 bit value
var N = parseBigInt("d4c7f8a2b32c11b8fba9581ec4ba4f1b04215642ef7355e37c0fc0443ef756ea2c6b8eeb755a1c723027663caa265ef785b8ff6a9b35227a52d86633dbdfca43",16);
var g = parseBigInt("2",10);
var a = null;
var A = null;
var B = null;
var k = null;
var x = null;
var u = null;
var K = null;
var M = 0;
var M2 = null;
var salt = null;
var rng = null;
var Sc = null;
var one;
var two;
var three;
var radix = 16;
var proto = "6";   // 3 or 6 or 6a
var hash  = "SHA1";

var startTime = null;  //measure time of execution start
var endTime = null;    //measure time of execution end

var INITIATED     = "INITIATED";     //starting authentication submitting username
var INITIALIZED   = "INITIALIZED";    //username submitted
var INPROCESS     = "INPROCESS";      //submitted M1
var AUTHENTICATED   = "AUTHENTICATED";   //allok
var FAILEDAUTH     = "FAILEDAUTH";      //failed
var STATE = null;
var NULL=null;  //<-- added
var twoFactType  = null;  //what type of two factor is used
var towFactChlg  = null;

var submit_enckx = false;

//PHONE AUTH VARIABLES
//3 minutes
//var PHONE_AUTH_POLLING_TIMEOUT = 180000;
var PUSH_AUTH_MAX_ATTEMPTS = 36;  //36*5s = 180s=3min
var PUSH_AUTH_ATTEMPTS = 0;
//5 seconds
var PUSH_CHECK_AUTH_INTERVAL = 5000
//POLLING URL
var PUSH_AUTH_POLLING_URL ='';
var PUSH_AUTH_STATE ='INVALID';
var PUSH_AUTH_INSTRUCTIONS = 'A message has been sent to your phone. Tap the link to complete secondary authentication.<br>';
var PUSH_AUTH_ALT_LINK = 'Click here if you did not receive the message.';
var PUSH_TIMEOUT_MSG = 'Two Factor Authentication Timed out.';

var pushTimeout;

var VERSION = 0;

//Local storage key
var SELECTED_SF_KEY="SELECTED_SF";
//URL param to reset LS
var RESET_KEY="resetSF";
//SF types
var SSC = "3";
var IBKEY_ANDROID = "5.2a";
var IBKEY_IOS = "5.2i";
var BANK_KEY = "5.3";
var DSC = "4.1";
var ALPINE = "4";
var DSC_PLUS = "5.1";
var PLAT_GOLD = "5";
var TSC = "6";
var SMS = "4.2";

var SF_NAMES ={};
SF_NAMES[SSC] = "Security Code Card";
SF_NAMES[IBKEY_ANDROID] = "IB Key";
SF_NAMES[IBKEY_IOS] = "IB Key";
SF_NAMES[BANK_KEY] = "Bank Key";
SF_NAMES[DSC] = "Digital Security Card";
SF_NAMES[ALPINE] = "Alpine Device";
SF_NAMES[DSC_PLUS] = "Digital Security Card+";
SF_NAMES[PLAT_GOLD] = "Platinum/Gold";
SF_NAMES[TSC] = "Temporary Security Code";
SF_NAMES[SMS] ="One Time Passcode"

var tempServerS2="";

window.IBSSO = window.IBSSO || {};
IBSSO.MESSAGES = IBSSO.MESSAGES || {};
IBSSO.CONSTANT = IBSSO.CONSTANT || {};

IBSSO.MESSAGES.LIVE_ACCOUNT_WITH_PAPER_TRADING = "You have selected the Live Account Mode, but the specified user is a Paper Trading user. Please select the correct Login mode.";
IBSSO.isPaper = IBSSO.isPaper || {};

IBSSO.CONSTANT.LOGIN_TYPE_PROD = 1;
IBSSO.CONSTANT.LOGIN_TYPE_PAPER = 2;

IBSSO.CONSTANT.SECOND_FACTOR_TYPE_OTP = "4.2";
IBSSO.CONSTANT.OTP_DELIVERY_TYPE_SMS = 1;
IBSSO.CONSTANT.OTP_DELIVERY_TYPE_VOICE = 2;
IBSSO.CONSTANT.OTP_DELIVERY_TYPE_EMAIL = 4;

IBSSO.otpSelectTimer = null;
IBSSO.otpSelectTimerTimeout = 60 * 1000; //60sec


var loadingCallback = function(){
  //Quick check for url parameter to reset local store
  var resetSF = getParam(RESET_KEY);
  if(resetSF && resetSF=="true"){
    try {
      localStorage.setItem(SELECTED_SF_KEY, "");
    } catch(e) {}
  }
  if(typeof SF_VERSION != "undefined" && SF_VERSION){
    VERSION = SF_VERSION;
  }
};

var specialPushAuthRequest;
var specialCompleteAuth_1;

if(document.readyState ==="complete"||(document.readySate !=="loading" && !document.documentElement.doScroll)){
  loadingCallback();
}else{
  document.addEventListener("DOMContentLoaded",loadingCallback);
}
//ibkey recovery variables

var MODE_NORMAL = "NORMAL";
var MODE_IBKEY_RECOVERY = "IBKEY_RECOVERY";
var MODE = MODE_NORMAL;

function isync_radix() {
  radix = 16;
}

function isync_proto() {
  proto = "6";
}


function parseBigInt(str,r) {
  return new BigInteger(str,r);
}

function bigInt2radix(bi, radix){
  return bi.toString(radix);
}

function randomBigInt(bytes) {
  if(rng == null) {
    rng = new SecureRandom();
  }
  return new BigInteger(8 * bytes, rng);
}

function str2BigInt(str) {
  return parseBigInt(str, radix);
}

function bigInt2Str(bi) {
  return bigInt2radix(bi, radix);
}

/* Returns a string with n zeroes in it */
function nzero(n) {
  if(n < 1) {
    return "";
  }
  var t = nzero(n >> 1);
  if((n & 1) == 0) {
    return t + t;
  }
  else {
    return t + t + "0";
  }
}

function set_random_a() {
  a = randomBigInt(32);
  if(a.compareTo(N) >= 0) {
    a = a.mod(N.subtract(one));
  }
  if(a.compareTo(two) < 0) {
    a = two;
  }
  //for tests keep it static
  // a = parseBigInt("222444666",16);
}

function randomize_a() {
  set_random_a();
  recalc_A();
}

function recalc_A() {
  A = g.modPow(a, N);
}

function recalc_k() {
  k = xyz_compute_k(N, g);
}

function recalc_K() {
 // writeDebug("recalc_K Sc = "+bigInt2radix(Sc,16));
  K = xyz_compute_K(Sc);
 // writeDebug("recalc_K K = "+K);
}

function recalc_M(){
  M = xyz_compute_M1(N, g,user_name, salt, A, B, K);
 // writeDebug("recalc_M M = "+M);
}

function recalc_M2(){
  M2 = xyz_compute_M2(A,M,K);
}

function recalc_x() {
 // writeDebug("recalc_x  user = "+getUserName());
 // writeDebug("recalc_x  pass = "+getPassword());
 // writeDebug("recalc_x  salt = "+bigInt2radix(salt,16));
  x = xyz_compute_x(getUserName(), getPassword(), salt);
 // writeDebug("recalc_x x = "+bigInt2radix(x ,16));
}

function recalc_Sc() {
 // writeDebug("recalc_Sc B = "+bigInt2radix(B, 16));
 // writeDebug("recalc_Sc x = "+bigInt2radix(x, 16));
 // writeDebug("recalc_Sc u = "+bigInt2radix(u, 16));
 // writeDebug("recalc_Sc a = "+bigInt2radix(a, 16));
 // writeDebug("recalc_Sc k = "+bigInt2radix(k, 16));
  Sc = xyz_compute_client_S(B, x, u, a, k);
 // writeDebug("recalc_Sc Sc = "+bigInt2radix(Sc, 16));
}

function recalc_u() {
 // writeDebug("recalc_u N = "+bigInt2radix(N,16));
 // writeDebug("recalc_u A = "+bigInt2radix(A, 16));
 // writeDebug("recalc_u B = "+bigInt2radix(B, 16));
  u = xyz_compute_u(N, A, B);
 // writeDebug("recalc_u u = "+u);
}

function recalc_v(){
  // writeDebug("recalc_v g = "+g);
  // writeDebug("recalc_v x = "+x);
   var v = xyz_compute_v(g,x);
  // writeDebug("recalc_v v = "+v);
}

/* S = (B - kg^x) ^ (a + ux) (mod N) */
function xyz_compute_client_S(BB, xx, uu, aa, kk) {
  var bx = g.modPow(xx, N);
  var btmp = BB.add(N.multiply(kk)).subtract(bx.multiply(kk)).mod(N);
  return btmp.modPow(xx.multiply(uu).add(aa), N);
}

function xyz_compute_k(NN, gg) {
  var hashin = "";
  var nhex;
  var ghex;
  var ktmp;
  if(proto == "3")
    return one;
  else if(proto == "6")
    return three;
  else {
    /* XYZ-6a: k = H(N || g) */
    nhex = String(bigInt2radix(NN, 16));
    if((nhex.length & 1) == 0) {
      hashin += nhex;
    }
    else {
      hashin += "0" + nhex;
    }
    ghex = String(bigInt2radix(gg, 16));
    hashin += nzero(nhex.length - ghex.length);
    hashin += ghex;
    ktmp = parseBigInt(calcSHA1Hex(hashin), 16);
    if(ktmp.compareTo(NN) < 0) {
      return ktmp;
    }
    else {
      return ktmp.mod(NN);
    }
  }
}

/* x = H(salt || H(username || ":" || password)) */
function xyz_compute_x(u, p, s) {
  // Inner hash: SHA-1(username || ":" || password)
  var ih = calcSHA1(u + ":" + p);
  // Outer hash: SHA-1(salt || inner_hash)
  // This assumes that the hex salt string has an even number of characters...
  //original
  //var oh = calcSHA1Hex(bigInt2radix(s, 16) + ih);
  //this makes sure that hex salt string has an even number of characters, kt ...
   var oh = calcSHA1Hex(verifyHexVal(s) + ih);
  var xtmp = parseBigInt(oh, 16);
  if(xtmp.compareTo(N) < 0) {
    return xtmp;
  }
  else {
    return xtmp.mod(N.subtract(one));
  }
}

/*
 * XYZ-3: u = first 32 bits (MSB) of SHA-1(B)
 * XYZ-6(a): u = SHA-1(A || B)
 */

function xyz_compute_u(Nv, av, bv) {
  var ahex;
  var bhex = String(bigInt2radix(bv, 16));
  var hashin = "";
  var utmp;
  var nlen;
  if(proto != "3") {
    ahex = String(bigInt2radix(av, 16));
    if(proto == "6") {
      if((ahex.length & 1) == 0) {
        hashin += ahex;
      }
      else {
        hashin += "0" + ahex;
      }
    }
    else { /* 6a requires left-padding */
      nlen = 2 * ((Nv.bitLength() + 7) >> 3);
      hashin += nzero(nlen - ahex.length) + ahex;
    }
  }
  if(proto == "3" || proto == "6") {
    if((bhex.length & 1) == 0) {
      hashin += bhex;
    }
    else {
      hashin += "0" + bhex;
    }
  }
  else { /* 6a requires left-padding; nlen already set above */
    hashin += nzero(nlen - bhex.length) + bhex;
  }
  if(proto == "3") {
    utmp = parseBigInt(calcSHA1Hex(hashin).substr(0, 8), 16);
  }
  else {

    utmp = parseBigInt(calcSHA1Hex(hashin), 16);
  }
  if(utmp.compareTo(Nv) < 0) {
    return utmp;
  }
  else {
    return utmp.mod(Nv.subtract(one));
  }
}

/* K = H(S) */
function xyz_compute_K(SS) {
  var tmpk = calcSHA1Hex(verifyHexVal(SS));
  // writeDebug("xyz_compute_K K = "+tmpk);
  return tmpk;
}

// M = H(A,B,K) */
function xyz_compute_M_old(AI,BI,KI) {
  var hashin ="";
  hashin +=  String(bigInt2radix(verifyHexVal(AI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(BI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(KI), 16));

  return calcSHA1Hex(hashin);
}

// M1 = H( H(N) xor H(g), H(I), s, A,B,K) */
function xyz_compute_M1(NI, gI, uI, sI, AI,BI,KI) {
  var hashin ="";
  //  writeDebug("xyz_compute_M1 N: "+NI);
  var h_n = calcSHA1Hex(bigInt2radix(verifyHexVal(NI), 16));
  // writeDebug("xyz_compute_M1 h_n: "+h_n);
  //  writeDebug("xyz_compute_M1 g: "+gI);

  var h_g = calcSHA1Hex(bigInt2radix(verifyHexVal(gI), 16));
  // writeDebug("xyz_compute_M1 h_g: "+h_g);
  h_n = parseBigInt(verifyHexVal(h_n), 16);
  h_g = parseBigInt(verifyHexVal(h_g), 16);

  var h_xor = verifyHexVal(bigInt2radix(h_n.xor(h_g),16));
  // writeDebug("xyz_compute_M1 xor: "+h_xor);

  // writeDebug("xyz_compute_M1 username: "+uI);
  var h_I = calcSHA1(uI);
  // writeDebug("xyz_compute_M1 h_I: "+h_I);

  var shex =  verifyHexVal(sI);
  // writeDebug("xyz_compute_M1 s_hex: "+shex);
  // writeDebug("xyz_compute_M1 AI : "+bigInt2radix(verifyHexVal(AI), 16));
  // writeDebug("xyz_compute_M1 BI : "+bigInt2radix(verifyHexVal(BI), 16));
  // writeDebug("xyz_compute_M1 KI : "+bigInt2radix(verifyHexVal(KI), 16));

  hashin +=  h_xor;
  hashin +=  h_I;
  hashin +=  shex;
  hashin +=  String(bigInt2radix(verifyHexVal(AI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(BI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(KI), 16));
  // writeDebug("xyz_compute_M1 hashin= "+hashin);
  return verifyHexVal(calcSHA1Hex(hashin));
}

function xyz_compute_M2(AI,MI,KI) {
  //alert("A: "+AI+"\nMI: "+MI+"\nKI: "+KI);
  var hashin ="";
  hashin +=  String(bigInt2radix(verifyHexVal(AI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(MI), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(KI), 16));

  return calcSHA1Hex(hashin);
}

/* v = g^x */
function xyz_compute_v(gi, xi) {
 return gi.modPow(xi, N);
}

/* this makes sure it is even number  */
function verifyHexVal(HV){
  var hashin ="";
  var bhex = String(bigInt2radix(HV, 16));
  if((bhex.length & 1) == 0) {
    hashin += bhex;
  }
  else {
    hashin += "0" + bhex;
  }

  //20061106
  if(hashin.charAt(0) == "0" && hashin.charAt(1) == "0"){
    hashin = hashin.substring(2,hashin.length);
  }
  return hashin;
}

function set_radix(r) {
  radix = r;
}

function set_proto(p) {
  proto = p;
  recalc_k();
}

//read url params
function getParam(param){
  var location = window.location.search;
  var parArr = location.split('?').length==2 ? location.split('?')[1].split("&"): [];
  for(var i=0;i<parArr.length;i++){
    parr = parArr[i].split('=');
    if(parr[0] == param){
      return parr[1];
    }
  }
}

function xyz_initialize() {
  rng_seed_time();
  if(initialized > 0)
  {
    return;
  }
  one = parseBigInt("1", 16);
  two = parseBigInt("2", 16);
  three = parseBigInt("3", 16);

  randomize_a();
  initialized = 1;
}

var userNameFocused = false;
var passwordFocused = false;
var forceUserNameFocused = false;
var forcePasswordFocused = false;
var userNameEnterPressed = false;

function focusElsewhereFirst() {
  document.getElementById("submitForm").focus();//<-- As a work around to deal with hidden focus problem
}

function focusUserName() {
  if(!userNameFocused && !forcePasswordFocused) {
    writeDebug("##focusUserName");
    document.getElementById(user_field_id).focus();
    userNameFocused = true;
    passwordFocused = false;
  }
}

function focusPassword() {
  if(!passwordFocused && !forceUserNameFocused) {
    writeDebug("##focusPassword");
    document.getElementById(pass_field_id).focus();
    passwordFocused = true;
    userNameFocused = false;
  }
}

function forceFocusUserName() {
  writeDebug("##forceFocusUserName: "+STATE);
  showErrorMessage(error_field_id,"");
  forceUserNameFocused = true;
  forcePasswordFocused = false;
  document.getElementById(user_field_id).focus();
}

function forceFocusPassword() {
  writeDebug("##forceFocusPassword: "+STATE);
  showErrorMessage(error_field_id,"");
  forceUserNameFocused = false;
  forcePasswordFocused = true;
  document.getElementById(pass_field_id).focus();
}


function enableUserNameSubmit(){
  writeDebug("enableUserNameSubmit: "+STATE);
  document.getElementById("submitForm").disabled=false;
  return true;
}

function handleUserNameEvent(evt){
  evt = (evt)?evt : event;
  var charCode = (evt.charCode)? evt.charCode : ((evt.which) ?evt.which:evt.keyCode);
  if(charCode == 13 || charCode == 3){
    writeDebug("*** username enter pressed "+charCode);
    userNameEnterPressed = true;
    complete_authentication = false;
    passwordFocused = false;
    forceUserNameFocused = false;
    focusPassword();
  }
  return false;
}

function initAuthentication(in_user_id, in_error_id){
  writeDebug("initAuthentication: "+STATE);

  //initAuthentication() requires xyz_initialize() to have completed successfully
  if(initialized === 0){
    writeDebug("Warning: initAuthentication() called before xyz_initialize(). Calling xyz_initialize() now.");
    xyz_initialize();
  }

  setUserFieldId(in_user_id);
  setErrorFieldId(in_error_id);

  if(user_name != null && user_name != getUserName()){
    writeDebug("user_name != getUserName: "+STATE);
    STATE = null;
    XYZ = true;
  }
  else if(user_name != null && user_name == getUserName() && STATE != FAILEDAUTH){
    writeDebug("user_name == getUserName: "+STATE);
    return false;
  }

  //alert("initAuthentication");

  user_name = getUserName();
  acct_id = getAcctId();
  force_login=getForceLogin();
  if(user_name == null || user_name == ""){
    //showErrorMessage(error_field_id,"Enter username");
    return false;
  }
  else{
    //showErrorMessage(error_field_id,"");
  }
  if(STATE == INITIATED){
    return false;
  }
  writeDebug("initAuthentication 2 : "+STATE);
  STATE = INITIATED;

  var login_type = getLoginType();

  var params = "ACTION=INIT&APP_NAME="+app_name+"&MODE="+MODE+"&FORCE_LOGIN="+force_login+"&USER="+user_name+"&ACCT="+ acct_id + "&A="+bigInt2Str(A);
  if (login_type) {
    params += "&LOGIN_TYPE=" + login_type;
  }
  var loader = new xhr.ContentLoader("POST", url, true, initHandler, params);
}

function processError(root, from){
  if(root !=null && root.getElementsByTagName("error") !=null && root.getElementsByTagName("error").length>0){
    var message = root.getElementsByTagName("error")[0].firstChild.nodeValue;
    //send separately link that has 'LINK_S' and 'LINK_E'
    if(root.getElementsByTagName("link").length>0){
      var url = root.getElementsByTagName("link")[0].firstChild.nodeValue;
      message = message.replace("LINK_E","</a>");
      message = message.replace("LINK_S","<a href='"+url+"'>");
    }
    if(message == "unknown"){
      if(getPassword().length > 0){
        showErrorMessage(error_field_id,"Invalid username password combination");
        clearUserName();
        clearPassword();
        usernameFocus();
      }
    }
    else if (from === "initHandler" && message === "Invalid username password combination") {
      writeDebug("processError - Do not display error message with empty password");
      // Do not display this error message if password field is empty
      // see https://prod.ibkr-web.com/ibjira/browse/IGAME-1244
    }
    else{
      showErrorMessage(error_field_id,message);
    }
    STATE = null;

    return true;
  }
  return false;
}


function initHandler(){
  writeDebug("initHandler : "+STATE);
  if (!this.req || !this.req.responseXML) {
    return false;
  }
  var root = this.req.responseXML.documentElement;
  if(processError(root, "initHandler")){
    return false;
  }
  else if(this.req.responseText.indexOf("RESPONSEPENDING")!=-1){
    return false;
  }

  var default_param = true;

  if (!root.getElementsByTagName("user") || !root.getElementsByTagName("user")[0]) {
    return false;
  }
  var user = root.getElementsByTagName("user")[0].firstChild.nodeValue;
  if(user != "true"){
    //in transitional period switch to use old type authentication
    XYZ = false;
    var isloginform = xyz_form_id == "loginform";
    if(!isloginform){
      showErrorMessage(error_field_id,"Invalid username password combination");
      return false;
    }
    else{
      return true;
    }
  }

  try {
    if (root.getElementsByTagName("paper").length > 0) {
      var paper = root.getElementsByTagName("paper")[0].firstChild.nodeValue;
      var user = getUserName();
      if (user && paper) {
        // save isPaper state for username
        IBSSO.isPaper[user] = (paper === "true");
      }
    }
  } catch(e) {}

  var newg = root.getElementsByTagName("g")[0].firstChild.nodeValue;
  var newN = root.getElementsByTagName("N")[0].firstChild.nodeValue;
  proto = root.getElementsByTagName("proto")[0].firstChild.nodeValue;
  hash = root.getElementsByTagName("hash")[0].firstChild.nodeValue;
  salt = parseBigInt(root.getElementsByTagName("s")[0].firstChild.nodeValue,16);
  B = parseBigInt(root.getElementsByTagName("B")[0].firstChild.nodeValue,16);
  try{
    suppLongPwd = ("true" === root.getElementsByTagName("lp")[0].firstChild.nodeValue);
  }catch(err){suppLongPwd = false;}//default not provided

  if(root.getElementsByTagName("rsapub")!=null && root.getElementsByTagName("rsapub").length > 0 && root.getElementsByTagName("rsapub")[0].firstChild !=null){
    submit_enckx = true;
    rsa = new  RSAKey();
    rsa.setPublic(root.getElementsByTagName("rsapub")[0].firstChild.nodeValue,'3');
  }
  if(root.getElementsByTagName("info")!=null && root.getElementsByTagName("info").length > 0 && root.getElementsByTagName("info")[0].firstChild !=null){
    var message = root.getElementsByTagName("info")[0].firstChild.nodeValue;
    showErrorMessage(error_field_id,message);
  }

  if(!g.equals(parseBigInt(newg,10))){
      default_param = false;
    g = parseBigInt(newg,10);
  }
  if(!N.equals(parseBigInt(newN,16))){
    default_param = false;
    N = parseBigInt(newN,16);
  }
  if(!default_param){
    STATE = NULL;
    initAuthentication();
    return false;
  }

  recalc_k();
  STATE = INITIALIZED;
  writeDebug("initHandler complete_authentication: "+complete_authentication+" STATE: "+STATE);
  if(complete_authentication){
    writeDebug("** completeAuthentication invoked from initHandler");
    completeAuthentication();
  } else {
    if(!bypass_empty_field) {
      writeDebug("initHandler focusPassword");
      focusPassword();
    }
  }
}


function completeAuthentication(in_passw_id, in_error_id){

  if(!bypass_empty_field) {
    if(!complete_authentication) {
      return false;
    }
  }

  writeDebug("completeAuthentication: "+STATE);
  setPasswFieldId(in_passw_id);
  setErrorFieldId(in_error_id);
  var counter = 0;
  //here logic is like this if it is not INITIALIZED it is probobly due to 2 reasons:
    // 1. server has not responded (when it will respond complete auth.
    // 2. STATE == null - in this case run init and the whole authentication from beginning.
  if(!XYZ){return true;} //this user doesn't have XYZ use old authentication

  if(STATE == null || STATE == FAILEDAUTH){
    //(STATE !=INITIATED && STATE != INITIALIZED && STATE != INPROCESS && STATE != AUTHENTICATED )){
    complete_authentication = false;
    initAuthentication();//do the whole thing;
    return false;
  }
  if(STATE == INITIATED){  //this if submit form is entered and server hasn't come back with xyz params
    complete_authentication = true;
    return false;
  }
  if(STATE == INPROCESS){
    //there is already thread running wait till it finishes
    return false;
  }

  if(!bypass_empty_field) {
    if(getPassword() == null || getPassword() == ""){
        showErrorMessage(error_field_id,"Please enter password");
        writeDebug("password focus: "+STATE);
        focusPassword()
        return false;
      }
  }
  STATE = INPROCESS;
  showErrorMessage(error_field_id,"");
  recalc_x();
  recalc_u();
  recalc_Sc();
  recalc_K();
  recalc_M();

  var login_type = getLoginType();

  var params ="ACTION=COMPLETEAUTH&APP_NAME="+app_name+"&USER="+user_name+"&ACCT="+ acct_id + "&M1="+M+"&VERSION="+VERSION;
  if (login_type) {
    params += "&LOGIN_TYPE=" + login_type;
  }
  if(submit_enckx){
    var ekx = do_encrypt(K);
    writeDebug("EKX: "+ekx);
    params +="&EKX="+ekx;
  }
  if(VERSION == 1){
    var loader = new xhr.ContentLoader("POST", url,true, completeAuthenticationHandlerV2,params);
  }else{
    var loader = new xhr.ContentLoader("POST", url,true, completeAuthenticationHandler,params);
  }
  return false;
}

//second auth step. completeAuthentication() must have been sent previously to generate variables
function  completeAuthentication_1(){
  if(typeof specialCompleteAuth_1 !== "undefined" && typeof specialCompleteAuth_1 === "function"){
    specialCompleteAuth_1();
  } else {
    //addition of SF parameter
    var params ="ACTION=COMPLETEAUTH_1&APP_NAME="+app_name+"&USER="+user_name+"&ACCT="+ acct_id + "&M1="+M+"&VERSION="+VERSION+"&SF="+selectedSF;
    var loader = new xhr.ContentLoader("POST", url,true, completeAuthenticationHandler,params);
  }
}

function completeAuthenticationHandlerV2(){
  writeDebug("completeAuthenticationHandlerV2: "+STATE+" "+this.req.responseText.length);
  var root = this.req.responseXML.documentElement;

  if(this.req.responseText.length ==0 ||root.getElementsByTagName("error").length>0){

    var message = root.getElementsByTagName("error")[0].firstChild.nodeValue;
    showErrorMessage(error_field_id,message);

    STATE = null;
    return false;
  }
  else if(this.req.responseText.indexOf("RESPONSEPENDING")!=-1){
    return false;
  }

  var pushSent = false;
  var server_M2="";
  if(root.getElementsByTagName("M2").length>0){
    server_M2 = root.getElementsByTagName("M2")[0].firstChild.nodeValue;
    tempServerS2 = server_M2;
  }else{
    //from previous request take temps2
    server_M2 = tempServerS2;
  }

  var twoFactorType = null;

  if(root.getElementsByTagName("type").length >0){
    twoFactorType = root.getElementsByTagName("type")[0].firstChild.nodeValue;
  }

  try{
    pushSent = (root.getElementsByTagName("push_sent")[0].firstChild.nodeValue === "true");
  }catch(err){pushSent = false;}//default not provided

  recalc_M2();
  complete_authentication = false; //reset to initial state
  if(server_M2 == M2){
    //calculate session key
    //store it in the cookie
    var sk = compute_sk(B,K)
    recordSK(sk);
    showErrorMessage(error_field_id,"");
    //if two factor authentication is required, show the form

    twoFactorUser=true;

  //read second factor types
    if(root.getElementsByTagName("sftypes").length >0){
      //TODO handle/get multiple sf types
      var sfTypes = root.getElementsByTagName("sftypes")[0].firstChild.nodeValue;
      userSfTypes = sfTypes.split(',');
      //check length of userSfTypes

      if(userSfTypes && userSfTypes.length==1){
        //single selection and not show second factor list
        selectedSF = userSfTypes[0];
        completeAuthentication_1();
      }else{
        showSecondFactorList();
      }
      return;
    }


    //get reference to the form
    //append M1
    //submit the form

    STATE=AUTHENTICATED;

    //Added by snarayan for 'Confirm Token Action'
    if(complete_confirm_token && !is_two_factor_user) {
      randomizePassword();
      var user_name = getUserName();
      var token = fortifyFilterOpenRedirect(document.getElementById("token").value);
      complete_confirm_token = false;
      //if(getIfMultiUser() == "true" ) {
      //  var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
        //  generalAjaxQuery(url, myparams);
        //  return true;
        //} else {
        //    var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
      //    generalAjaxQuery(url, myparams);
      //    return true;
        //}


      //Display all the signature parts for ACH signature
      var signatures = document.getElementsByName("signatures");
      var signatures_str = "";
      if(signatures!=null) {
        showErrorMessage("ACHERRORMSG","");
        //signatures_str = "&signaturelength="+signatures.length;

        for(var i =0;i<signatures.length;i++) {
          //alert("signatures: "+signatures[i].value);
          signatures_str = signatures_str+"&signatures"+i+"="+escape(signatures[i].value);
        }
        signatures_str = "&signaturelength="+signatures.length+signatures_str;
        //alert(signatures_str);
      }

      if(getIfMultiUser() == "true" ) {
      var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
      if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      } else {
          var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
          if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      }


    }
    //End of add by snarayan

    if(debug){
      showErrorMessage(error_field_id,"");
      return true;
    }
    var my_form = document.getElementById(xyz_form_id);
    //handle login
    //alert(" before my_form != null "+STATE);

    if(my_form != null){
      appendM1andM2(xyz_form_id);
      //this form logs in
      if(submit_form){
        //alert(" submitting my form "+STATE);

         randomizePassword();
         xyzLoginformSubmit(my_form);
      }
    }

    return true;
  }
  else{
    if(root.getElementsByTagName("reached_max_login")!=null && root.getElementsByTagName("reached_max_login")[0] !=null && root.getElementsByTagName("reached_max_login")[0].firstChild !=null) {
    var reached_max_login = root.getElementsByTagName("reached_max_login")[0].firstChild.nodeValue;
    if(reached_max_login=="true") {
      var my_form = document.getElementById(xyz_form_id);
      showErrorMessage(error_field_id,"Invalid username password combination, reached max limit. Redirecting...");
      xyzLoginformSubmit(my_form);
    }
    }
    showErrorMessage(error_field_id,"Invalid username password combination");
    STATE=FAILEDAUTH;
    clearUserName();
    clearPassword();
    usernameFocus();
    return false;

  }

}

function onChangeSelectSF(el){

  //make request for second factor
  var value = el.value;
  //check value isn't blank
  if(value==""){
    return;
  }
  selectedSF=value;
  try {
    //set local store
    localStorage.setItem(SELECTED_SF_KEY, selectedSF);
  } catch(e) {}
  //display/forward to handle of second factor
  completeAuthentication_1();
}

function showSecondFactorList(){

  //check local storage
  var selectedSecondFactor = "";
  try {
    selectedSecondFactor = localStorage.getItem(SELECTED_SF_KEY);
  } catch(e) {}
  //read userSfTypes
  var html ="<select style=\"width:inherit;\" class=\"custom-dropdown__select custom-dropdown__select--white\" id=\"sf_select\" onChange=\"onChangeSelectSF(this)\"> ";
  html +="<option value =\"\">Select Type</option>";
  var foundSelected = false;
  for(var a=0;a<userSfTypes.length;a++){
    //numerical type
    var typeNum = userSfTypes[a];
    var name = SF_NAMES[typeNum];
    var selected = (selectedSecondFactor == typeNum ? "selected":"");

    if(selected.length>0){
      foundSelected = true;
    }
    html +="<option value =\""+ typeNum +"\"" + selected + ">" + name + "</option>";
  }
  html +="</select>";

  var sfContainer = document.getElementById("sfContainer");
  var twofactSelect = document.getElementById("twofactselect");

  sfContainer.innerHTML= html;
  twofactSelect.style.display = "";
  //append to
  //attempt to auto select value
  if(foundSelected){
    selectedSF = selectedSecondFactor;
    completeAuthentication_1();
  }
}


function completeAuthenticationHandler(){
  writeDebug("completeAuthenticationHandler: "+STATE+" "+this.req.responseText.length);
  var root = this.req.responseXML.documentElement;

  if(this.req.responseText.length ==0 ||root.getElementsByTagName("error").length>0){

    var message = root.getElementsByTagName("error")[0].firstChild.nodeValue;
    showErrorMessage(error_field_id,message);


    STATE = null;
    return false;
  }
  else if(this.req.responseText.indexOf("RESPONSEPENDING")!=-1){
    return false;
  }

  var pushSent = false;
  var server_M2="";
  if(root.getElementsByTagName("M2").length>0){
    server_M2 = root.getElementsByTagName("M2")[0].firstChild.nodeValue;
  }else{
    //from previous request take temps2
    server_M2 = tempServerS2;
  }

  var twoFactorType = null;

  if(root.getElementsByTagName("type").length >0){
    twoFactorType = root.getElementsByTagName("type")[0].firstChild.nodeValue;
  }

  try{
    pushSent = (root.getElementsByTagName("push_sent")[0].firstChild.nodeValue === "true");
  }catch(err){pushSent = false;}//default not provided

  recalc_M2();
  complete_authentication = false; //reset to initial state
  if(server_M2 == M2){
    //calculate session key
    //store it in the cookie
    var sk = compute_sk(B,K)
    recordSK(sk);
    showErrorMessage(error_field_id,"");
      //if two factor authentication is required, show the form
    if(twoFactorType != null){
      twoFactorUser=true;
      doTwoFactorAuthentication(root, pushSent);
      if(pushSent){
        //If specialPushAuthRequest() is defined, then execute that. Otherwise, default to pushAuthRequest(root)
        if(typeof specialPushAuthRequest !== "undefined" && typeof specialPushAuthRequest === "function"){
          specialPushAuthRequest(root);
        } else {
          pushAuthRequest(root);
        }
      }
      return;
    }

    //get reference to the form
    //append M1
    //submit the form

    STATE=AUTHENTICATED;

    //Added by snarayan for 'Confirm Token Action'
    if(complete_confirm_token && !is_two_factor_user) {
      randomizePassword();
      var user_name = getUserName();
      var token = fortifyFilterOpenRedirect(document.getElementById("token").value);
      complete_confirm_token = false;
      //if(getIfMultiUser() == "true" ) {
      //  var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
        //  generalAjaxQuery(url, myparams);
        //  return true;
        //} else {
        //    var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
      //    generalAjaxQuery(url, myparams);
      //    return true;
        //}


      //Display all the signature parts for ACH signature
      var signatures = document.getElementsByName("signatures");
      var signatures_str = "";
      if(signatures!=null) {
        showErrorMessage("ACHERRORMSG","");
        //signatures_str = "&signaturelength="+signatures.length;

        for(var i =0;i<signatures.length;i++) {
          //alert("signatures: "+signatures[i].value);
          signatures_str = signatures_str+"&signatures"+i+"="+escape(signatures[i].value);
        }
        signatures_str = "&signaturelength="+signatures.length+signatures_str;
        //alert(signatures_str);
      }

      if(getIfMultiUser() == "true" ) {
      var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
      if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      } else {
          var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
          if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      }
    }
    //End of add by snarayan

    if(debug){
      showErrorMessage(error_field_id,"");
      return true;
    }
    var my_form = document.getElementById(xyz_form_id);
    //handle login
    //alert(" before my_form != null "+STATE);

    if(my_form != null){
      appendM1andM2(xyz_form_id);
      //this form logs in
      if(submit_form){
        //alert(" submitting my form "+STATE);

         randomizePassword();
         xyzLoginformSubmit(my_form);
      }
    }

    return true;
  }
  else{
    if(root.getElementsByTagName("reached_max_login")!=null && root.getElementsByTagName("reached_max_login")[0] !=null && root.getElementsByTagName("reached_max_login")[0].firstChild !=null) {
    var reached_max_login = root.getElementsByTagName("reached_max_login")[0].firstChild.nodeValue;
    if(reached_max_login === "true") {
      var my_form = document.getElementById(xyz_form_id);
      showErrorMessage(error_field_id,"Invalid username password combination, reached max limit. Redirecting...");
      xyzLoginformSubmit(my_form);
    }
    }
    showErrorMessage(error_field_id,"Invalid username password combination");
    STATE=FAILEDAUTH;
    clearUserName();
    clearPassword();
    usernameFocus();
    return false;
  }
}

function doTwoFactorAuthentication(xmlroot, isPushAuth){
  var div1 = document.getElementById("authtable");
  var tbodypos  = document.getElementById("twofactauth");
  var tbodybase = document.getElementById("twofactbase");
  if (typeof tbodypos != "undefined" && tbodypos) {
    div1.replaceChild(tbodybase, tbodypos);
  }

  var chlgtext  = document.getElementById("chlgtext");
  var chlgdescr = document.getElementById("chlgdescr");
  var chlgfield = document.getElementById("chlginput");
  chlgfield.focus();
  chlgfield.select();

  //delete chlginput1 if present
  if(document.getElementById("chlginput1")!=null){
    document.getElementById("chlginput1").remove();
  }
  try
  {
      var submitbttn = document.getElementById("submitForm");
      submitbttn.diabled = false;
      submitbttn.onclick = authenticateTwoFactor;
  }
  catch(e) {}

  twoFactType = xmlroot.getElementsByTagName("type")[0].firstChild.nodeValue;

  //hide push if present
  hideIBKeyInst();
  //make sure twofactbase present
  document.getElementById("twofactbase").style.display='';
  //clear timeout
  clearPush();
  if (twoFactType == "SWTK")
  {
      var swtkText = "Security Code";
      //clear instructions
      chlgtext.innerHTML="";
      if (document.getElementById("chlgdescr_SWTK"))
          swtkText = document.getElementById("chlgdescr_SWTK").textContent;

      if (xyz_html5_placeholderCheck())
      {
          chlgfield.setAttribute("placeholder", swtkText);

          if(chlgdescr){
            chlgdescr.parentNode.removeChild(chlgdescr);
          }
      }
      else
          chlgdescr.innerHTML = swtkText;
      attachXYZHelpLink(twoFactType);
      checkOTPSelection(xmlroot);
  }
  else if (twoFactType == "SWCR")
  {
      twoFactChlg = xmlroot.getElementsByTagName("challenge")[0].firstChild.nodeValue;
      if (document.getElementById("chlgtext_SWCR"))
      {

        if(document.getElementById("chlg_SWCR")){
          document.getElementById("chlg_SWCR").remove();
        }
        var challenge = document.createElement("span");
        challenge.id="chlg_SWCR";
        challenge.innerHTML=twoFactChlg;
        document.getElementById("chlgtext_SWCR").appendChild(challenge);
        document.getElementById("chlgtext_SWCR").style.display = "";
        chlgtext.style.display = "";
      }
      else {
        chlgtext.innerHTML = "<label>Challenge: " + twoFactChlg + "</label>";
        chlgtext.style.display = "";
      }

      var swcrText = "Response String";
      if (isPushAuth) {
	      if (document.getElementById("chlgdescr_SWCR_IBKey")) {
	          swcrText = document.getElementById("chlgdescr_SWCR_IBKey").textContent;
	      }
      } else {
	      if (document.getElementById("chlgdescr_SWCR")) {
	          swcrText = document.getElementById("chlgdescr_SWCR").textContent;
	      }
      }

      var usePlaceHolder = xyz_html5_placeholderCheck();
      if (usePlaceHolder)
      {
          chlgfield.setAttribute("placeholder", swcrText);
          //reset size of challenge field

          if(chlgdescr){
            chlgdescr.parentNode.removeChild(chlgdescr);
          }
      }
      else
          chlgdescr.innerHTML = swcrText;
     if (!isPushAuth) {
      attachXYZHelpLink(twoFactType);
   }
  }
  else if (twoFactType == "Bingo")
  {
    //reset first
    chlgtext.innerHTML="";
      if (document.getElementById("chlgtext_Bingo"))
          document.getElementById("chlgtext_Bingo").style.display = "";
      else
          chlgtext.innerHTML = "<label>Use your security code card for authentication. Two card index numbers are shown in the image below. For each index, look up the corresponding value, and enter in the field below.<br/><br/>Index Numbers<br/></label>";
      var img = new Image();
      img.src = url + "?ACTION=GET_BINGO_CHLG&APP_NAME=" + app_name + "&USER=" + user_name + "&rand=" + Math.random();
      chlgtext.appendChild(img);
      chlgtext.style.display = "";

      var bingoText = "Card Values";
      if (document.getElementById("chlgdescr_Bingo"))
          bingoText = document.getElementById("chlgdescr_Bingo").textContent;

      if (xyz_html5_placeholderCheck())
      {
          chlgfield.setAttribute("placeholder", bingoText);

          if(chlgdescr){
            chlgdescr.parentNode.removeChild(chlgdescr);
          }
      }
      else
          chlgdescr.innerHTML = bingoText;
      attachXYZHelpLink(twoFactType);
  }
  else if (twoFactType == "IBTK")
  {
    twoFactChlg = xmlroot.getElementsByTagName("challenge")[0].firstChild.nodeValue;

    var ibtkText = "Temporary Security Code";
    //clear instructions
    chlgtext.innerHTML="";
    if (document.getElementById("chlgdescr_IBTK"))
        ibtkText = document.getElementById("chlgdescr_IBTK").textContent;


    if (xyz_html5_placeholderCheck())
    {
      chlgfield.setAttribute("placeholder", ibtkText);

      if(chlgdescr){
        chlgdescr.parentNode.removeChild(chlgdescr);

      }
    }
    else
        chlgdescr.innerHTML = ibtkText;
      attachXYZHelpLink(twoFactType);
  }

  if (isPushAuth) {
    // hide and display phone text
    tbodybase.style.display = 'none';
  }

  if (isPushAuth && !IBSSO.isRL) {
    // add hide show link
    var ibKeyInst = document.getElementById('ibKeyInst');
    ibKeyInst.innerHTML=PUSH_AUTH_INSTRUCTIONS;
    ibKeyInst.style.display='';
    //create and add link
    var link = document.createElement('a');
    link.id = 'phoneLink'
    link.href = "javascript:void(0)";
    link.onclick = hideShowChallenge;
    link.innerHTML = PUSH_AUTH_ALT_LINK;
    ibKeyInst.appendChild(link);
    ibKeyInst.appendChild(createHelpLink(twoFactType));
  }
  
  if (isPushAuth && IBSSO.isRL) {
	  $('.login-step-username').hide();
	  $('.login-step-sf').hide();
	  $('.login-step-notification').show();
	  animateNotification();
  }
}

function notificationShowChallenge() {
  clearTimeout(pushTimeout);
  $('.login-step-notification').hide();
  $('.login-step-challenge').show();
  $('.login-step-sf').show();
  $('#twofactbase').show();
}

function resendNotification() {
  completeAuthentication_1();
  $('.login-step-notification .notifResent').slideDown();
};

function animateNotification() {
  $('.notificationImg').attr('src', 'images/2fa-animated-once.gif?q=' + Math.random());
}

function xyz_html5_placeholderCheck()
{
    var test = document.createElement("input");
    return ("placeholder" in test);
}

function hideIBKeyInst(){
  var ibKeyInst = document.getElementById("ibKeyInst");
  if(ibKeyInst != null)
   ibKeyInst.style.display ='none';
}

function hideShowChallenge(){
  var tbodybase = document.getElementById("twofactbase");
  var instructions2 = document.getElementById("phoneLink");

  if(tbodybase.style.display =='none'){
    tbodybase.style.display ='';
  }else{
    tbodybase.style.display ='none';
  }
}

// PHONE AUTH FUNCTIONS
function pushAuthRequest(root) {

  // set root to reuse on timeout
  if (root) {
    PHONE_AUTH_ROOT = root;
  }
  PUSH_AUTH_ATTEMPTS++;
  // TODO replace params
  if(VERSION==1){
    var params = "ACTION=COMPLETETWOFACT&APP_NAME="+app_name+"&USER="+ user_name+"&VERSION="+VERSION+"&SF="+selectedSF+"&PUSH=true"+"&counter="+PUSH_AUTH_ATTEMPTS;
  } else {
    var params = "ACTION=COMPLETETWOFACT&APP_NAME="+app_name+"&USER="+ user_name+"&VERSION="+VERSION+"&SF="+twoFactType+"&PUSH=true"+"&counter="+PUSH_AUTH_ATTEMPTS;
  }
  var loader = new xhr.ContentLoader("POST", url, true, pushAuthHandler, params);
  // PHONE_AUTH_REQUESTED = true;
}

function pushAuthHandler() {
  // Read XML doc
  var root = this.req.responseXML.documentElement;

  // check response valid
  // not valid call pushAuthRequest again
  // also check if max attempts reached
  if (PUSH_AUTH_ATTEMPTS >= PUSH_AUTH_MAX_ATTEMPTS) {
    // display error message or show challenege inputs
    // TODO change error message
    showErrorMessage(error_field_id, '<i class="fa fa-exclamation-circle"></i> ' + PUSH_TIMEOUT_MSG);
    hideShowChallenge();
    return;
  }

  if(root.getElementsByTagName("auth_res")!=null){
    var auth_res = root.getElementsByTagName("auth_res")[0].firstChild.nodeValue;
    if(auth_res === "true"){
      PUSH_AUTH_ATTEMPTS = 0;
      authenticateTwoFactorHandler(root);
    }
    else{
      pushTimeout = setTimeout(pushAuthRequest, PUSH_CHECK_AUTH_INTERVAL);
    }
  }
}

function clearPush(){
  //reset to zero
  PUSH_AUTH_ATTEMPTS = 0;
  clearTimeout(pushTimeout);
}

function createHelpLink(twoFactType,secondHelp){
  var suffix = "";
  if(secondHelp){
    suffix = "2";
  }
  var aLink = document.createElement("a");
    aLink.style.fontSize = "14px";
    aLink.style.color = "#999999";
    aLink.style.cssFloat = "right";
    aLink.setAttribute("href", "#");
    aLink.setAttribute("onclick", "showXYZHelpWindow('" + twoFactType + "'); return false;");
    aLink.setAttribute("id", "help_SF" + suffix);
    aLink.setAttribute("name", "help_" + twoFactType);
    if (document.getElementById("helpTextCopy"))
        aLink.innerHTML = document.getElementById("helpTextCopy").innerHTML;
    else
        aLink.innerHTML = "Help";
    return aLink;
}

function attachXYZHelpLink(twoFactType){
  try {
    var url = (top.frames) ? top.frames.location : document.location;
    url = url.toString().toLowerCase();
    var chlgfield = document.getElementById("chlginput");
    if (url && chlgfield && url.indexOf("interactivebrokers") != -1)
    {
      var parent = chlgfield.parentNode;
      if (parent)
      {
        //delete old sf
        if(document.getElementById('help_SF2')!=null){
          document.getElementById('help_SF2').remove();
        }
        //check if IBTK
        if(twoFactType=='IBTK'){
          return;
        }
        var aLink = createHelpLink(twoFactType,true);

        parent.appendChild(aLink);
        chlgfield.focus();
        chlgfield.select();
      }
    }
  } catch(e) {}
}

function addSecondInputResponse(){
  var noSpaceChal = twoFactChlg.replace(/ /gi, '');
  var added = noSpaceChal.length == 6;

  if (added ) {
    //delete chlginput1 if present

    var responseInput = document.getElementById("chlginput");
    responseInput.maxLength = 5;
    responseInput.size = 5;
    responseInput.style.width = "90px";
    responseInput.style.marginLeft = "25px";
    responseInput.onkeyup = challengeKeyUpCheck;
    //remove placeholder
    responseInput.placeholder="";
    responseInput.autocomplete="off";

    var responseInput1 = document.createElement("input");
    responseInput1.maxLength = 3;
    responseInput1.size = 3;
    responseInput1.style.width = "40px";
    responseInput1.id = "chlginput1";
    responseInput1.disabled = true;
    responseInput1.style.marginLeft = "10px";
    responseInput1.autocomplete="off";
    responseInput.parentElement.appendChild(responseInput1);
  }

  return added;
}

function challengeKeyUpCheck(e){
  //enable second response and change focus
  var responseInput = document.getElementById('chlginput').value;
  var responseInput1 = document.getElementById('chlginput1');
  if(responseInput.length==5){
    var responseInput1 = document.getElementById('chlginput1');
    responseInput1.disabled= false;
    responseInput1.focus();
  }
}

function showXYZHelpWindow(type){
  var twoFactType = type || this.name.split("_")[1];
  var helpPlatinum = "https://www.interactivebrokers.com/en/general/platinumHelp.php";
  var helpDSC      = "https://www.interactivebrokers.com/en/general/digital_security_device.php";
  var helpBingo    = "https://www.interactivebrokers.com/en/general/bingoHelp.php";
  var helpGold     = "https://www.interactivebrokers.com/en/index.php?f=5775";
  var helpSMS      = "https://ibkr.info/article/3196#login";
  var helpIBKey    = "https://www.interactivebrokers.com/en/index.php?f=13733";

  if(twoFactType == "SWTK"){
    if(selectedSF == IBSSO.CONSTANT.SECOND_FACTOR_TYPE_OTP){
      window.open(helpSMS, "HelpSMS", "width=800,height=700,scrollbars=yes,resizable=yes");
    } else {
      window.open(helpDSC, "HelpSWTK", "width=800,height=700,scrollbars=yes,resizable=yes");
    }
  }else if(twoFactType == "SWCR"){
    //check if gold
    var noSpaceChal = typeof twoFactChlg === 'string' ? twoFactChlg.replace(/ /gi,'') : '';
    if(selectedSF ==IBKEY_ANDROID || selectedSF ==IBKEY_IOS){
      window.open(helpIBKey, "HelpSWCR", "width=800,height=700,scrollbars=yes,resizable=yes");
      return;
    }
    if(noSpaceChal.length==6){
      window.open(helpGold, "HelpSWCR", "width=800,height=700,scrollbars=yes,resizable=yes");
    }else{
      window.open(helpPlatinum, "HelpSWCR", "width=800,height=700,scrollbars=yes,resizable=yes");
    }
  }else if(twoFactType == "Bingo"){
    window.open(helpBingo, "HelpBingo", "width=800,height=700,scrollbars=yes,resizable=yes");
  }else if(twoFactType == "IBTK"){
    window.open(helpPlatinum, "HelpIBTK", "width=800,height=700,scrollbars=yes,resizable=yes");
  }
}

function authenticateTwoFactor(){
  document.getElementById("submitForm").disabled = true;
  var additionalChal="";
  if(document.getElementById("chlginput1")){
    additionalChal = document.getElementById("chlginput1").value;
  }
  var resp = fortifyFilterOpenRedirect(document.getElementById("chlginput").value + additionalChal);
  if(twoFactType !=null && twoFactType == "IBTK"){
    var num = "";
    var ctmp = parseBigInt(twoFactChlg,16);
    //num = "112628796121458";
     //var ntmp = parseBigInt(num,10);

    num ="";
    for(var i=0;i<resp.length;i++){
      num += ""+dec2Hex(resp.charCodeAt(i));
    }

    ntmp = num;

    var hashin =  String(bigInt2radix(verifyHexVal(ctmp), 16));
        hashin +=  String(bigInt2radix(verifyHexVal(ntmp), 16));
    resp = verifyHexVal(calcSHA1Hex(hashin));

  }
  var sfParam = VERSION==1 ? selectedSF : twoFactType;
  var manualIbKey = (sfParam === IBKEY_ANDROID || sfParam === IBKEY_IOS) ? "&manualIbKey=true" : "";
  var params = "ACTION=COMPLETETWOFACT&APP_NAME=" + app_name + "&USER=" + user_name + "&ACCT=" + acct_id + 
    "&RESPONSE=" + resp + "&VERSION=" + VERSION + "&SF=" + sfParam + manualIbKey;
  var loader = new xhr.ContentLoader("POST", url,true, authenticateTwoFactorHandler,params);
}

function authenticateTwoFactorHandler(){
  var my_form = document.getElementById(xyz_form_id);

  if(my_form != null){
    appendM1andM2(xyz_form_id);
    randomizePassword();
    xyzLoginformSubmit(document.getElementById(xyz_form_id));
    return true;
  }
}

function appendM1andM2(formname){
  var my_form = document.getElementById(formname);
      //handle login
  if(my_form != null){
    var checkForM1 = document.getElementsByName("M1");
    if (checkForM1.length)
        checkForM1[0].parentNode.removeChild(checkForM1[0]);

    var cs = document.createElement("input");
    cs.type = "hidden";
    cs.name = "M1";
    cs.id = "M1";
    cs.value = M;
    my_form.appendChild(cs);

    var checkForM2 = document.getElementsByName("M2");
    if (checkForM2.length)
        checkForM2[0].parentNode.removeChild(checkForM2[0]);

    var ss = document.createElement("input");
    ss.type = "hidden";
    ss.name = "M2";
    ss.id = "M2";
    ss.value = M2;
    my_form.appendChild(ss);
  }
}

var bypass_empty_field = false;

function bypass_empty_field_check() {
  bypass_empty_field = true;
  return false;
}

//this is the form that is invoked from authentication forms
// In order to show proper error messages and shifting focus as per twiki http://websrv3/twiki/bin/view/Main/LoginBlankFieldHandling
// more conditions are added in this method but these are not required in invoked from pages like password change
// the variable bypass_empty_field is set to true for the regular flow and for error messages it it set to false. By default it is false
function submit_xyz_form(in_xyz_form_id, in_user_id, in_passw_id, in_error_id){

  if(!bypass_empty_field) {
    if(getUserName()== null || getUserName()== ""){
      showErrorMessage(error_field_id,"Please enter Username and Password");
      writeDebug("Please enter Username and Password submit_xyz_form: "+STATE+" error_field_id: "+error_field_id);
      forceUserNameFocused = false;
      forcePasswordFocused = false;
      userNameFocused = false;
      passwordFocused = false;
      clearUserName();
      //clearPassword();
      focusUserName();
      return false;
    }

    if(userNameEnterPressed) {
      writeDebug("* userNameEnterPressed " );
      userNameEnterPressed = false;
      forceUserNameFocused = false;
      forcePasswordFocused = false;
      userNameFocused = false;
      passwordFocused = false;
      focusPassword();
      return false;
    }

    if(getPassword() == null || getPassword() == ""){
      showErrorMessage(error_field_id,"Please enter password");
      writeDebug("Please enter Password submit_xyz_form: "+STATE+" error_field_id: "+error_field_id);
      forceUserNameFocused = false;
      forcePasswordFocused = false;
      userNameFocused = false;
      passwordFocused = false;
      focusPassword();
      return false;
    }

    complete_authentication = true;
  }

  if (!checkUsernameWithLoginType()) {
    writeDebug("checkUsernameWithLoginType failed");
    enableSubmit();
    alert(IBSSO.MESSAGES.LIVE_ACCOUNT_WITH_PAPER_TRADING);
    return false;
  }

  setXyzFormId(in_xyz_form_id);
  setUserFieldId(in_user_id);
  setPasswFieldId(in_passw_id);
  setErrorFieldId(in_error_id);

  writeDebug("submit_xyz_form: "+STATE);
  submit_form  = true;
  if(!XYZ){
    return true;
  }
  if(STATE==AUTHENTICATED){
      //state is authenticate reset password
      randomizePassword();
    return true;
  }
  else if(STATE==FAILEDAUTH){
    //failed authentication start from beginning
    STATE=null;
    completeAuthentication(in_passw_id,in_error_id);
    return false;
  }
  else{
    //go to completeAuthentication depending on state it will complete
    writeDebug("completeAuthentication submit_xyz_form: " + STATE + " error_field_id: " + error_field_id);
    completeAuthentication(in_passw_id,in_error_id);
    return false;
  }
}

function checkUsernameWithLoginType() {
  var valid = true;

  var login_type = getLoginType();
  var paper = IBSSO.isPaper[getUserName()];

  if (paper !== undefined) { // we have data to username
    if (login_type == IBSSO.CONSTANT.LOGIN_TYPE_PROD && paper) {
      valid = false;
    }
  }

  return valid;
}

function modifyPassword(){

  var pass_ver = verify_password_modification();
  if(!pass_ver){
    showErrorMessage(error_field_id,"Invalid new passwords");
    return;
  }
  else if(STATE == AUTHENTICATED){
    showErrorMessage(error_field_id,"");
    var npass = getNewPassword();
    var epass = do_encrypt(getNewPassword());
    var params = "ACTION=MODIFYPASS&APP_NAME="+app_name+"&USER="+getUserName()+"&token="+getToken()+"&epass="+epass+"&M1="+M;
    var loader = new xhr.ContentLoader("POST", url,true, modifyPasswordHandler,params);
    return;
  }
  else{
    complete_authentication = true;
    completeAuthentication();

    return false;
  }
}

function modifyPasswordHandler(){
  var root = this.req.responseXML.documentElement;

  var pass_modif_res = root.getElementsByTagName("pass_modif_res")[0].firstChild.nodeValue;
  if(pass_modif_res == "NULL"){
    password_was_changed();
    return;
  }
  else{
    showErrorMessage(error_field_id,passwordChangeCodes[pass_modif_res]);
  }
}

function requestPasswordToken(tokenURL){
  var params = "ACTION=TOKEN&APP_NAME="+app_name;
  var loader = new xhr.ContentLoader("POST", tokenURL,true, requestPasswordTokenHandler,params);
}

function requestPasswordTokenHandler(){
  var root = this.req.responseXML.documentElement;
  var token_sending_res = root.getElementsByTagName("token_sending_res")[0].firstChild.nodeValue;
  showErrorMessage(error_field_id,token_sending_res);
}

function modifyPasswordByOperator(){
  if(!verify_password_modification()){
    showErrorMessage(error_field_id,"Invalid New Passwords");
    return false;
  }
  var epass = do_encrypt(getNewPassword());
  var params = "ACTION=MODIFYPASS&APP_NAME="+app_name+"&user_name="+user_name+"&epass="+epass;
  var loader = new xhr.ContentLoader("POST", url,true, modifyPasswordByOperatorHandler,params);
}

function modifyPasswordByOperatorHandler(){
  var root = this.req.responseXML.documentElement;

  var pass_modif_res = root.getElementsByTagName("pass_modif_res")[0].firstChild.nodeValue;
  if(pass_modif_res == "NULL"){ //no errors
    showErrorMessage(error_field_id,"");
    password_was_changed();
    return;
  }
  else{
    showErrorMessage(error_field_id,"Password was not changed. "+passwordChangeCodes[pass_modif_res]);
  }
}

function canDisableLoginForm(){
  if(STATE == null)  {return false;}
  else if(!XYZ)    {return false;}
  else if((STATE==INITIATED) && userNameEnterPressed ) {return false;}
  else        {return true;}
}

function dec2Hex(dec){

  dec = parseInt(dec,10);
  if(!isNaN(dec)){
    hexChars ="0123456789abcdef";
    if(dec > 255){return "";}
    var i = dec % 16;
    var j = (dec -i)/16;
    var result = hexChars.charAt(j)+hexChars.charAt(i);
    return result;

  }
  else return "";
}

//Functions required for Signature Component


var token_field_id   = "token";
var in_xyz_form_id   = "confirmform";
var actual_user_field_id   = "actual_user_name";
var pass_field_id   = "password";
var two_factor_user_id = "twofactoruser";
var multi_user_id = "multiuser";

function getToken(){
  return fortifyFilterOpenRedirect(document.getElementById(token_field_id).value);
}

function getActualUserName(){
  return document.getElementById(actual_user_field_id).value;
}

function getIfTwoFactorUser(){
  return document.getElementById(two_factor_user_id).value;
}

function getIfMultiUser(){
  return document.getElementById(multi_user_id).value;
}

function doNothing() {
  return true;
}

function authenticateTwoFactorSign(){
  //document.getElementById("submitForm").disabled = true;
  var additionalChal="";
  if(document.getElementById("chlginput1")){
      additionalChal = document.getElementById("chlginput1").value;
  }

  var resp = fortifyFilterOpenRedirect(document.getElementById("chlginput").value + additionalChal);
  if(twoFactType !=null && twoFactType == "IBTK"){
    var num = "";
    var ctmp = parseBigInt(twoFactChlg,16);
    //num = "112628796121458";
     //var ntmp = parseBigInt(num,10);

    num ="";
    for(var i=0;i<resp.length;i++){
      num += ""+dec2Hex(resp.charCodeAt(i));
    }

    ntmp = num;

    var hashin =  String(bigInt2radix(verifyHexVal(ctmp), 16));
    hashin +=  String(bigInt2radix(verifyHexVal(ntmp), 16));
    resp = verifyHexVal(calcSHA1Hex(hashin));
  }

  var params = "ACTION=COMPLETETWOFACT&APP_NAME="+app_name+"&USER="+user_name+"&RESPONSE="+resp+"&VERSION="+VERSION+"&SF="+selectedSF;
  var loader = new xhr.ContentLoader("POST", url,true, authenticateTwoFactorHandlerSign,params);
}

function authenticateTwoFactorHandlerSign(){
  var root = this.req.responseXML.documentElement;
  var res = root.getElementsByTagName("auth_res")[0].firstChild.nodeValue;
  var my_form = document.getElementById(xyz_form_id);
  res = res.replace(/^\s+/,'');
  res = res.replace(/\s+$/,'');
  if(my_form != null){
    appendM1andM2(xyz_form_id);
    randomizePassword();
    if(res == "true")
      showErrorMessage(error_field_id,"");
    else  {
      showErrorMessage(error_field_id,"Error While Two Factor Authentication");
      document.getElementById("confirmrequestform").submit();
    }
    return true;
  }

}


function confirmTwoFactorAndSign(myURL, myparams){
  writeDebug("generalAjaxQuery: myURL: "+myURL);
  writeDebug("generalAjaxQuery: myparams: "+myparams);
  var loader = new xhr.ContentLoader("POST", myURL,true, generalAjaxQueryHandler,myparams);
}

function generalAjaxQuery(myURL, myparams){
  writeDebug("generalAjaxQuery: myURL: "+myURL);
  var loader = new xhr.ContentLoader("POST", myURL,true, generalAjaxQueryHandler,myparams);
}
function clearToken(){
  document.getElementById("token").value ="";
}

function generalAjaxQueryHandler(){
  var root = this.req.responseXML.documentElement;
  var confirmAndVerify = root.getElementsByTagName("confirmAndVerify")[0].firstChild.nodeValue;
  //alert("generalAjaxQueryHandler ");
  if(confirmAndVerify=="true") {
    showErrorMessage(error_field_id,"Confirmed and Signature Verified");
    document.confirmrequestform.submit();
  } else if(confirmAndVerify=="achfalse") {
    showErrorMessage("ACHERRORMSG","Please Check Signature(s)");
    clearUserName();
    clearPassword();
    usernameFocus();
  } else if(confirmAndVerify=="achdepositfalse") {
    showErrorMessage(error_field_id,"Error While Submitting ACH Deposit");
    clearUserName();
    clearPassword();
    usernameFocus();
  } else {
    if(getIfTwoFactorUser() == "true" ) {
      showErrorMessage(error_field_id,"Error While Two Factor Authentication");
      document.confirmrequestform.submit();
    } else {
      showErrorMessage(error_field_id,"Please Check Confirmation Number");
    }
    STATE="FAILEDCONFIRM";//required so that the 'confirmRequest' would start by completeAuthentication again
    clearUserName();
    clearPassword();
    clearToken();
    usernameFocus();
  }
}

function blockEnter (evt) {
  evt  = (evt) ? evt : event;
  var charCode = (evt.charCode)? evt.charCode:
    ((evt.which)? evt.which : evt.keyCode);
  if(charCode == 13 || charCode ==3) {
    return false;
  } else {
    return true;
  }
}


function checkUser(url, redirect_url) {
  var user_name = getUserName();
  var token = getToken();
  var actual_user_name = getActualUserName();
  if(actual_user_name==user_name) {
    showErrorMessage("MYERRORMSG","");
    return true;
  } else {
    if(user_name!=null && user_name!="") {
      showErrorMessage("MYERRORMSG","Error: Username mismatch");
    }
    clearUserName();
    clearPassword();
    usernameFocus();
    return false;
  }
}

function requestNewToken(url, request_user_name, request_id) {
  var myparams = "request_id="+request_id+"&ACTION=RESENDREQUESTTOKEN&APP_NAME="+app_name+"&request_user_name="+request_user_name;
  var loader = new xhr.ContentLoader("POST", url,true, requestNewTokenHandler,myparams);
}

var request_error_field_id   = "REQUESTERRORMSG";

function requestNewTokenHandler(){
  var root = this.req.responseXML.documentElement;
  var resendrequesttoken = root.getElementsByTagName("resendrequesttoken")[0].firstChild.nodeValue;
  if(resendrequesttoken=="true") {
    showErrorMessage(request_error_field_id,"Email Sent");
  } else {
    showErrorMessage(request_error_field_id,"Error while sending email token");
  }
}

function confirmTwoFactorRequest(url, redirect_url) {
  var user_name = getUserName();
  var actual_user_name = getActualUserName();
  if(actual_user_name==user_name) {
    showErrorMessage("MYERRORMSG","");
    authenticateTwoFactorSignNew();
    return true;
  } else {
    if(user_name!=null && user_name!="") {
        showErrorMessage("MYERRORMSG","Error: Username mismatch");
      }
      clearUserName();
    clearPassword();
    usernameFocus();
    return false;
  }
}

var donotsubmit = false;

function confirmTwoFactorAchDepositRequest(url, redirect_url) {
  var user_name = getUserName();
  var actual_user_name = getActualUserName();
  if(actual_user_name==user_name) {
    showErrorMessage("MYERRORMSG","");
    donotsubmit = true;
    authenticateTwoFactorSignNew();
    return true;
  } else {
    if(user_name!=null && user_name!="") {
        showErrorMessage("MYERRORMSG","Error: Username mismatch");
      }
      clearUserName();
    clearPassword();
    usernameFocus();
    return false;
  }
}

function confirmAchDepositRequest(url, redirect_url, in_passw_id, in_error_id) {
  debug = false;
  var user_name = getUserName();
  var token = 0;
  var actual_user_name = getActualUserName();

  complete_confirm_token = true;
  completeAuthentication(in_passw_id, in_error_id);
}

function confirmRequest(url, redirect_url) {
  debug = false;
  var user_name = getUserName();
  var token = getToken();
  var actual_user_name = getActualUserName();
  if(actual_user_name==user_name) {

    if(STATE == INPROCESS) {
      setTimeout('doNothing()', 200);
      return true;
    }

    setXyzFormId(in_xyz_form_id);
    setUserFieldId(user_field_id);
    setPasswFieldId(pass_field_id);
    setErrorFieldId(error_field_id);
    submit_form  = false;
    if(STATE==AUTHENTICATED && !complete_confirm_token) {
        //state is authenticate reset password
        randomizePassword();
        //if(getIfMultiUser() == "true" ) {
      //  var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
        //  generalAjaxQuery(url, myparams);
        //} else {
        //    var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
      //    generalAjaxQuery(url, myparams);
        //}


      //Display all the signature parts for ACH signature
      var signatures = document.getElementsByName("signatures");
      var signatures_str = "";
      if(signatures!=null) {
        showErrorMessage("ACHERRORMSG","");
        //signatures_str = "&signaturelength="+signatures.length;

        for(var i =0;i<signatures.length;i++) {
          //alert("signatures: "+signatures[i].value);
          signatures_str = signatures_str+"&signatures"+i+"="+escape(signatures[i].value);
        }
        signatures_str = "&signaturelength="+signatures.length+signatures_str;
        //alert(signatures_str);
      }

      if(getIfMultiUser() == "true" ) {
      var myparams = "token="+token+"&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
      if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      } else {
          var myparams = "token="+token+"&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
          if(signatures!=null) {
        myparams = myparams + signatures_str;
      }
        generalAjaxQuery(url, myparams);
      }

      return true;

    }
    else {
      //If the onBlur function of javascript is disabled, try to complete authentication
          //go to completeAuthetication, depending on state it will complete authentication
      complete_authentication = true;
      complete_confirm_token = true;
      if(getIfTwoFactorUser() == "true" ) {
        is_two_factor_user = true;
      }

      if(STATE != INITIATED){
        initAuthentication();//do the whole thing;
      } else {
        setTimeout('doNothing()', 200);
        completeAuthentication(pass_field_id,error_field_id);
      }
    }

  } else {
    showErrorMessage(error_field_id,"Error: Username mismatch");
    num_invoked=0;
    clearUserName();
    clearPassword();
    clearToken();
    usernameFocus();
  }
}


/////
//Functions for two factor signing with out token
function authenticateTwoFactorSignNew(){
  //document.getElementById("submitForm").disabled = true;
  //alert("authenticateTwoFactorSignNew");
  var additionalChal="";
  if(document.getElementById("chlginput1")){
    additionalChal = document.getElementById("chlginput1").value;
  }

  var resp = fortifyFilterOpenRedirect(document.getElementById("chlginput").value + additionalChal);

  //    var resp = fortifyFilterOpenRedirect(document.getElementById("chlginput").value);
  if(twoFactType !=null && twoFactType == "IBTK"){
    var num = "";
    var ctmp = parseBigInt(twoFactChlg,16);
    //num = "112628796121458";
    //var ntmp = parseBigInt(num,10);

    num ="";
    for(var i=0;i<resp.length;i++){
      num += ""+dec2Hex(resp.charCodeAt(i));
    }

    ntmp = num;

    var hashin =  String(bigInt2radix(verifyHexVal(ctmp), 16));
    hashin +=  String(bigInt2radix(verifyHexVal(ntmp), 16));
    resp = verifyHexVal(calcSHA1Hex(hashin));
  }

  var params = "ACTION=COMPLETETWOFACT&APP_NAME="+app_name+"&USER="+user_name+"&RESPONSE="+resp+"&VERSION="+VERSION+"&SF="+selectedSF;
  var loader = new xhr.ContentLoader("POST", url,true, authenticateTwoFactorHandlerSignNew,params);
}

function authenticateTwoFactorHandlerSignNew(){
  //alert("authenticateTwoFactorHandlerSignNew");

  var root = this.req.responseXML.documentElement;
  var res = root.getElementsByTagName("auth_res")[0].firstChild.nodeValue;
  var my_form = document.getElementById(xyz_form_id);
  res = res.replace(/^\s+/,'');
  res = res.replace(/\s+$/,'');
  if(my_form != null){
    appendM1andM2(xyz_form_id);
    randomizePassword();
    if(res == "true") {
      showErrorMessage(error_field_id,"");
      var user_name = getUserName();
        var actual_user_name = getActualUserName();
        if(actual_user_name==user_name) {
          //alert("getIfMultiUser()"+getIfMultiUser());

          //Added For Handling ACH signatures
          //Display all the signature parts for ACH signature
          var signatures = document.getElementsByName("signatures");
          var signatures_str = "";
          if(signatures!=null) {
            showErrorMessage("ACHERRORMSG","");
            //signatures_str = "&signaturelength="+signatures.length;

            for(var i =0;i<signatures.length;i++) {
              //alert("signatures: "+signatures[i].value);
              signatures_str = signatures_str+"&signatures"+i+"="+escape(signatures[i].value);
            }
            signatures_str = "&signaturelength="+signatures.length+signatures_str;
            //alert(signatures_str);
          }
          // End of add for ACH Signature

          if(getIfMultiUser() == "true" ) {
          var myparams = "token=&ACTION=CONFIRMREQUESTWITHAUTHTOKEN&APP_NAME="+app_name+"&user_name="+user_name;
            showErrorMessage("MYERRORMSG","");
            if(signatures!=null) {
            myparams = myparams + signatures_str;
          }
            generalAjaxQuery(url, myparams);
        } else {
          var myparams = "token=&ACTION=CONFIRMREQUEST&APP_NAME="+app_name+"&user_name="+user_name;
            showErrorMessage("MYERRORMSG","");
            if(signatures!=null) {
            myparams = myparams + signatures_str;
          }
            generalAjaxQuery(url, myparams);
        }
        } else {
          if(user_name!=null && user_name!="") {
            showErrorMessage("MYERRORMSG","Error: Username mismatch");
          }
          clearUserName();
        clearPassword();
        usernameFocus();
      }
    } else {
      showErrorMessage(error_field_id,"Error While Two Factor Authentication");
      if(donotsubmit==true) {
        donotsubmit = false;
      } else {
        document.confirmrequestform.submit();
      }
    }
    return true;
  }

}

function compute_sk(seed,verifier) {
  var hashin ="";
  hashin +=  String(bigInt2radix(verifyHexVal(seed), 16));
  hashin +=  String(bigInt2radix(verifyHexVal(verifier), 16));
  var ret = calcSHA1Hex(hashin);
  writeDebug("seed: "+bigInt2radix(verifyHexVal(seed), 16));
  writeDebug("ver: "+bigInt2radix(verifyHexVal(verifier), 16));

  writeDebug("res: "+ret);
  return ret;
}

//End of functions for two factor confirm with out token

$(document).ready(function() {
  $('body').on('click', '.otp-select-sms', selectOTPText);
  $('body').on('click', '.otp-select-voice', selectOTPVoice);
  $('body').on('click', '.otp-select-email', selectOTPEmail);
  $('body').on('click', '.otp-cancel', cancelOTPSelection);
  $('body').on('click', '.otp-opener-text', showOTPSelection);
});

function showOTPSelection() {
  writeDebug('showOTPSelection');
  $('#otp-delivery').show();
  $('#twofactbase').hide();
  window.clearTimeout(window.IBSSO.otpSelectTimer);
}

function selectOTPText() {
  writeDebug('selectOTPText');
  selectOTPDelivery(1);
}

function selectOTPVoice() {
  writeDebug('selectOTPVoice');
  selectOTPDelivery(2);
}

function selectOTPEmail() {
  writeDebug('selectOTPEmail');
  selectOTPDelivery(4);
}

function selectOTPDelivery(delivery) {
  $.ajax({
    method: "POST",
    url: url,
    data: {
      ACTION: "COMPLETEAUTH_1",
      APP_NAME: app_name,
      USER: user_name,
      ACCT: acct_id,
      M1: M,
      VERSION: VERSION,
      SF: selectedSF,
      OTP_DELIVERY: delivery
    },
    success: function(xml) {
      if(xml && xml.getElementsByTagName("delivery_description").length > 0) {
        var delivery_description = xml.getElementsByTagName("delivery_description")[0].firstChild.nodeValue;
        if (delivery_description) {
          // Update delivery description
          $('.otp-text').text(delivery_description);
        }
      }
    }
  });

  $('#otp-delivery').hide();
  $('#twofactbase').show();
}

function cancelOTPSelection() {
  writeDebug('cancelOTPSelection');
  $('#otp-delivery').hide();
  $('#twofactbase').show();
}

function checkOTPSelection(xml) {
  if (selectedSF !== IBSSO.CONSTANT.SECOND_FACTOR_TYPE_OTP) {
    return;
  }

  var delivery_description = '';
  var supported_media = 0;

  if(xml.getElementsByTagName("delivery_description").length > 0) {
    delivery_description = xml.getElementsByTagName("delivery_description")[0].firstChild.nodeValue;
  }
  if(xml.getElementsByTagName("supported_media").length > 0) {
    var supported_media_node = xml.getElementsByTagName("supported_media")[0].firstChild.nodeValue;
    supported_media = parseInt(supported_media_node, 10);
  }

  $('.otp-text').text(delivery_description);

  if (supported_media > 0) {
    var isSMSSupported = supported_media & IBSSO.CONSTANT.OTP_DELIVERY_TYPE_SMS;
    var isVoiceSupported = supported_media & IBSSO.CONSTANT.OTP_DELIVERY_TYPE_VOICE;
    var isEmailSupported = supported_media & IBSSO.CONSTANT.OTP_DELIVERY_TYPE_EMAIL;

    var has2buttons = (isSMSSupported && isVoiceSupported && !isEmailSupported) ||
      (isSMSSupported && !isVoiceSupported && isEmailSupported) ||
      (!isSMSSupported && isVoiceSupported && isEmailSupported);
    var has3buttons = isSMSSupported && isVoiceSupported && isEmailSupported;

    if (isSMSSupported) {
      $('.otp-select-sms').show();
    } else {
      $('.otp-select-sms').hide();
    }

    if (isVoiceSupported) {
      $('.otp-select-voice').show();
    } else {
      $('.otp-select-voice').hide();
    }

    if (isEmailSupported) {
      $('.otp-select-email').show();
    } else {
      $('.otp-select-email').hide();
    }

    if (has2buttons) {
      $('.otp-buttons').addClass('twobuttons');
    }
    if (has3buttons) {
      $('.otp-buttons').addClass('threebuttons');
    }

    $('.otp-opener-text').show();

    window.IBSSO.otpSelectTimer = window.setTimeout(showOTPSelectionDelayed, window.IBSSO.otpSelectTimerTimeout);
  }
}

function showOTPSelectionDelayed() {
  writeDebug('showOTPSelectionDelayed');

  // switch Text and Voice and Email buttons
  $('.otp-buttons').addClass('otp-buttons-reverse');

  // show buttons
  showOTPSelection();
}

function xyzLoginformSubmit(formElement) {
  if (typeof window.runPublishToken === 'function') {
    window.runPublishToken();
    window.setTimeout(function() {
      formElement.submit();
    }, 1000);
  } else {
    formElement.submit();
  }
}


/* File: Templates/javascript/xyzview.js */
//this js file deals with creatin of input forms for:
//authentication - createLoginForm
//set new password - createPasswordForm
//modify password - createModifyPasswordForm

//all of them rely on div element with id=authdiv being in the form if it does not exist it will create one.

//create <div id="authdiv"></div> in your html page
//specify servlet that you need to go against var invoke_servlet = "Authenticate";
//specify what action to be taken after Login - AcctManagementCode var access=forardTo...?
var app_context = null;
var token_required = true;  //some password modifications do not require token

var pass_req  = "Choose a password that is six to eight characters long and contains at "+
        "least one number and no spaces. Make sure it is difficult for others to guess!";
var pass_efe_web = "The password change will be effective immediate for the purpose of logging into our web site. ";

var pass_efe_tws =   " For the purpose of logging into the Trader Workstation however, "+
                  " password changes made before 5:30pm (New York Time) on any business "+
                  " day will become effective on the following business day. Password "+
                  " changes made after 5:30pm (New York Time) on any business day will "+
                  " not become effective until two business days after the change was made ";

var pass_changed ="Your Password has been changed. The change will be effective immediately "+
                " for the purpose of logging into our web site. ";


var passwordChangeCodes = new Array("NULL",
                  "INVALID_PASSWORD",
                  "REPEAT_PASSWORD",
                  "INVALID_TOKEN");


passwordChangeCodes["NULL"]       =   "Password Changed";
passwordChangeCodes["INVALID_PASSWORD"] =   "Invalid New Password";
passwordChangeCodes["REPEAT_PASSWORD"]   =   "Do not use old passwords";
passwordChangeCodes["INVALID_TOKEN"]   =   "Invalid Token";

var force_login_id   = "forceLogin";
var acct_field_id   = "acct_id";
var user_field_id   = "user_name";
var pass_field_id   = "password";
var npass_field_id   = "npassword";
var npass2_field_id = "npassword2";
var token_field_id   = "token";
var error_field_id   = "ERRORMSG";
var login_type_field_id = "loginType";
var xyz_form_id   = null;


function setUserFieldId(m_in){
  if(m_in != null && m_in != "undefined"){user_field_id = m_in;  }
}
function setPasswFieldId(m_in){
  if(m_in != null && m_in != "undefined"){pass_field_id = m_in;}
}
function setNPasswFieldId(m_in){
  if(m_in != null && m_in != "undefined"){npass_field_id = m_in;}
}
function setNPassw2FieldId(m_in){
  if(m_in != null && m_in != "undefined"){npass2_field_id = m_in;}
}
function setErrorFieldId(m_in){
  if(m_in != null && m_in != "undefined"){error_field_id = m_in;}
}
function setTokenFieldId(m_in){
  if(m_in != null && m_in != "undefined"){token_field_id = m_in;}
}
function setXyzFormId(m_in){
  if(m_in != null && m_in != "undefined"){xyz_form_id = m_in;}
}


function getAcctId(){
  if(document.getElementById(acct_field_id)!=null && document.getElementById(acct_field_id)!='undefined') {
     return fortifyFilterOpenRedirect(document.getElementById(acct_field_id).value);
  } else {
     return "";
  }
}

function getForceLogin(){
  if(document.getElementById(force_login_id)!=null && document.getElementById(force_login_id)!='undefined') {
    return fortifyFilterOpenRedirect(document.getElementById(force_login_id).value);
  } else {
    return "";
  }
}

function getUserName(){
  var username = convertToLowercase(document.getElementById(user_field_id).value);
  document.getElementById(user_field_id).value = username;
  return fortifyFilterOpenRedirect(username);
}

function convertToLowercase(str) {
  if (!str || typeof str !== 'string' || str.indexOf('@') !== -1) {
    return str;
  }
  return str.toLowerCase();
}

function getPassword(){
  if(suppLongPwd){
    return document.getElementById(pass_field_id).value;
  }else{
    //do the inteligent chop, if password longer than 8 and indication is that it is not supported
    var p = document.getElementById(pass_field_id).value;
    if(p!=null && p.length > 8){
      return p.substr(0,8);
    }
    return p;
  }
}

function getNewPassword(){
  return document.getElementById(npass_field_id).value;
}

function getNewPassword2(){
  return document.getElementById(npass2_field_id).value;
}

function getToken(){
  return fortifyFilterOpenRedirect(document.getElementById(token_field_id).value);
}

function getLoginType(){
  var login_type;
  try {
    var login_type_field = document.getElementById(login_type_field_id);
    if (login_type_field != null) {
      if (login_type_field.nodeName === "INPUT") {
        login_type = login_type_field.value; // <input type="hidden"> in RL page
      }
      if (login_type_field.nodeName === "SELECT") {
        login_type = login_type_field.options[login_type_field.selectedIndex].value;
      }
    }
  } catch(e) {}
  return +login_type;
}

function clearUserName(){
  //only if the field is enbled clear it.
    if(document.getElementById(user_field_id).type == "text"){
    document.getElementById(user_field_id).value ="";
  }
}

function clearPassword(){
  return document.getElementById(pass_field_id).value ="";
}

function randomizePassword(){
   // randomizeField(pass_field_id);
  clearPassword();
}

function randomizeField(fieldid){
  var currval = document.getElementById(fieldid).value;
  var newval = "";
  if(currval !=null && currval.length>0){
    for(var i=0;i<currval.length;i++){
      newval +="x";
    }
  }
  document.getElementById(fieldid).value = newval;
}

function clearNewPassword(){
  return document.getElementById(npass_field_id).value ="";
}

function clearNewPassowrd2(){
  return document.getElementById(npass2_field_id).value ="";
}

function usernameFocus(){
  if(document.getElementById(user_field_id) != null && document.getElementById(user_field_id).type == "text"){
  document.getElementById(user_field_id).disabled=false;
  document.getElementById(user_field_id).focus();
  }
}

function passwordFocus(){
  document.getElementById(pass_field_id).focus();
}

function createInputField(name, id, type){
  var input = document.createElement("input");
  input.type = type;
  input.name = name;
  input.id = id;

  return input;
}

//handles enter key in login
function handleKeystrokeLogin(evt){
  evt = (evt)?evt : event;
  var charCode = (evt.charCode)? evt.charCode : ((evt.which) ?evt.which:evt.keyCode);
  if(charCode == 13 || charCode == 3){
    //completeAuthentication();
  }
}

function verify_and_encrypt_password_fields(formid, errorfield, pass1_id,pass2_id){

  var num = /\d+/;

  var npass  = document.getElementById(pass1_id).value;
  var npass2 = document.getElementById(pass2_id).value;
  if(npass !=  npass2){
    showErrorMessage(errorfield,"Passwords are not the same");
    return false;
  }
  else if(npass == null || npass.length <8 ||  !npass.match(num)){
    showErrorMessage(errorfield,"Check password criteria");
    return false;
  }
  else{
    encryptFormFields(formid, pass1_id);
    encryptFormFields(formid, pass2_id);
  }
  return true;
}


/* encripts field with id fieldid and appends to form, setting initial value to  "" */
function encryptFormFields(formid, fieldid){
  var mform = document.getElementById(formid);
  var fv   = document.getElementById(fieldid).value;
  if(fv != null){
    var efv  = do_encrypt(fv);
    document.getElementById(fieldid).value = "";
    //randomizeField(fieldid);
    var input=document.getElementById("enc_"+fieldid);
    //Bug 50143 check if field already there not to continously append password values
    if(input==null){
      input = document.createElement("input");
      input.type = "hidden";
      input.name = "enc_"+fieldid;
      input.id   = "enc_"+fieldid;
      if(mform != null){
        mform.appendChild(input);
      }
    }
    input.value = efv;
  }
  return false;
}


//verify new passwords:
//passwords are the same
//password length is >6
//need to add some error flags

function verify_password_modification(){
  var npass  = getNewPassword();
  var npass2 = getNewPassword2();

  if(npass == null || npass.length <6 ){
    return false;
  }
  if(npass !=  npass2){
    return false;
  }

  return true;
}

function createPasswordFormChangeByOperator(){
  var passwdiv = document.getElementById("passwdiv");
  if(passwdiv == null){
    if(passwdiv == null){
      var mbody = document.getElementsByTagName("body")[0];
      passwdiv= document.createElement("div");
      passwdiv.id = "passwdiv";
      mbody.appendChild(passwdiv);
    }
  }

  var datatable = document.createElement("table");
  passwdiv.appendChild(datatable);

  var f_row = datatable.insertRow(datatable.rows.length);
  var f_col = f_row.insertCell(f_row.cells.length);
  var i_col = f_row.insertCell(f_row.cells.length);

  var infotable = document.createElement("table");
  var i_row1 = infotable.insertRow(infotable.rows.length);
  var i_col1 = i_row1.insertCell(i_row1.cells.length);
  i_col1.appendChild(document.createTextNode(pass_req));
  i_col1.className = "redEmph";
  var i_row2 = infotable.insertRow(infotable.rows.length);
  var i_col2 = i_row2.insertCell(i_row2.cells.length);
  i_col2.appendChild(document.createTextNode(pass_efe_web + pass_efe_tws));


  var authtable =   document.createElement("table");

  f_col.appendChild(authtable);
  i_col.appendChild(infotable);



  var row3 = authtable.insertRow(authtable.rows.length);
  var col_31 = row3.insertCell(row3.cells.length);
  col_31.appendChild(document.createTextNode("New Password:"));
  var npassw = createInputField("npassword","npassword","password");
  var col_32 = row3.insertCell(row3.cells.length);
  col_32.appendChild(npassw);

  var row4 = authtable.insertRow(authtable.rows.length);
  var col_41 = row4.insertCell(row4.cells.length);
  col_41.appendChild(document.createTextNode("Re-enter New Password:"));
  var npassw2 = createInputField("npassword2","npassword2","password");
  var col_42 = row4.insertCell(row4.cells.length);
  col_42.appendChild(npassw2);


  var row6 = authtable.insertRow(authtable.rows.length);
  var col_61 = row6.insertCell(row6.cells.length);
  col_61.appendChild(document.createTextNode(""));

  var col_62 = row6.insertCell(row6.cells.length);
  var subm =  createInputField("submit","submit","button");
  subm.size = 10;
  subm.value = "SUBMIT";
  subm.onclick = modifyPasswordByOperator;
  col_62.appendChild(subm);
}

function showErrorMessage(field_id, text){
  var error_field = document.getElementById(field_id);
  if(error_field != null){
    error_field.innerHTML = text;
    error_field.style.visability="visible";
  }
}

function writeDebug(text){
  if(debug){
    var msgbox = document.getElementById("debugarea");
    if(msgbox == null){
      msgbox = document.createElement("div");
      msgbox.id = "debugarea";
    }
    var content = msgbox.innerHTML;
    content += text+"<br/>\n";
    msgbox.innerHTML = content;
    return true;
  }
}

function GetCookie(name){
  var end = 0;
  var result = 0;
  var myCookie = " " + document.cookie + ";";
  var searchFor = " " + name + "=";
  var start = myCookie.lastIndexOf(searchFor);
  if (start > -1) {
    start += searchFor.length; end = myCookie.indexOf(";", start);
    if (end > -1)
      result = unescape(myCookie.substring(start, end));
  }
  return result;
}

function SetCookie(name, value, expires, domain){
  var expString = ((expires == 0) ? "" : ("; expires =" + expires.toGMTString()));
  var domainstr = "";
  if(domain!=null && domain.length>0){
    domainstr = "domain="+domain+";";
  }

  document.cookie = name + "=" + (value == 0 ? "" : escape(value)) + expString +"; path=/;"+domainstr + " ;SameSite=None; Secure";
}

function ClearCookie(name){
  var expDate = new Date();
  expDate.setTime(200000);
  SetCookie(name, 0, expDate);
}

function VerifyCookiesEnabled(){
  var name ="testCookie";
  var date = new Date();
  var value = date.getTime().toString();
  SetCookie(name, value, 0);
  var realValue = GetCookie(name);
  ClearCookie(name);
  if(realValue == 0 || realValue != value) {
    //alert("You currently have cookies disabled on your browser. You must enable cookies to proceed.");
    return false;
  }
  return true;
}

function recordSK(sk){
  var hostname = location.hostname;
  var domain = "";
  //check that letter c is in domain name
  if(hostname.indexOf(".")!= -1 && hostname.indexOf("c")!=-1){
    domain = hostname.substring(hostname.indexOf("."));
  }
  // alert(hostname+" "+domain);
  if(app_context!=null){
    SetCookie("XYZAB_"+app_context,sk,0, domain);
    if (app_context == "IBG.LOGIN") {
       SetCookie("XYZAB_AM.LOGIN",sk,0, domain);
    }

  }
  // alert(hostname+" "+domain);
  //set this for backwards compatibility TODO is to remove it
  SetCookie("XYZAB",sk,0, domain);
}


/* File: Templates/javascript/rsa.js */
// Depends on jsbn.js and rng.js

// convert a (hex) string to a bignum object
function parseBigInt(str,r) {
  return new BigInteger(str,r);
}

function linebrk(s,n) {
  var ret = "";
  var i = 0;
  while(i + n < s.length) {
    ret += s.substring(i,i+n) + "\n";
    i += n;
  }
  return ret + s.substring(i,s.length);
}

function byte2Hex(b) {
  if(b < 0x10)
    return "0" + b.toString(16);
  else   
    return b.toString(16);
}

// PKCS#1 (type 2, random) pad input string s to n bytes, and return a bigint
function pkcs1pad2(s,n) {
  if(n < s.length + 11) {
    alert("Message too long for RSA");
    return null;
  }
  var ba = new Array();
  var i = s.length - 1;
  while(i >= 0 && n > 0) ba[--n] = s.charCodeAt(i--);
  ba[--n] = 0;
  var rng = new SecureRandom();
  var x = new Array();
  while(n > 2) { // random non-zero pad
    x[0] = 0;
    while(x[0] == 0) rng.nextBytes(x);
    ba[--n] = x[0];
  }
  ba[--n] = 2;
  ba[--n] = 0;
  return new BigInteger(ba);
}

// "empty" RSA key constructor
function RSAKey() {
  this.n = null;
  this.e = 0;
  this.d = null;
  this.p = null;
  this.q = null;
  this.dmp1 = null;
  this.dmq1 = null;
  this.coeff = null;
}

// Set the public key fields N and e from hex strings
function RSASetPublic(N,E) {
  if(N != null && E != null && N.length > 0 && E.length > 0) {
    this.n = parseBigInt(N,16);
    this.e = parseInt(E,16);
  }
  else
    alert("Invalid RSA public key");
}

// Perform raw public operation on "x": return x^e (mod n)
function RSADoPublic(x) {
  return x.modPowInt(this.e, this.n);
}

// Return the PKCS#1 RSA encryption of "text" as an even-length hex string
function RSAEncrypt(text) {
  var m = pkcs1pad2(text,(this.n.bitLength()+7)>>3);
  if(m == null) return null;
  var c = this.doPublic(m);
  if(c == null) return null;
  var h = c.toString(16);
  if((h.length & 1) == 0) return h; else return "0" + h;
}

// Return the PKCS#1 RSA encryption of "text" as a Base64-encoded string
//function RSAEncryptB64(text) {
//  var h = this.encrypt(text);
//  if(h) return hex2b64(h); else return null;
//}

// protected
RSAKey.prototype.doPublic = RSADoPublic;

// public
RSAKey.prototype.setPublic = RSASetPublic;
RSAKey.prototype.encrypt = RSAEncrypt;
//RSAKey.prototype.encrypt_b64 = RSAEncryptB64;


/* File: Templates/javascript/base64.js */
var b64map="ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var b64pad="=";

function hex2b64(h) {
  var i;
  var c;
  var ret = "";
  for(i = 0; i+3 <= h.length; i+=3) {
    c = parseInt(h.substring(i,i+3),16);
    ret += b64map.charAt(c >> 6) + b64map.charAt(c & 63);
  }
  if(i+1 == h.length) {
    c = parseInt(h.substring(i,i+1),16);
    ret += b64map.charAt(c << 2);
  }
  else if(i+2 == h.length) {
    c = parseInt(h.substring(i,i+2),16);
    ret += b64map.charAt(c >> 2) + b64map.charAt((c & 3) << 4);
  }
  while((ret.length & 3) > 0) ret += b64pad;
  return ret;
}

// convert a base64 string to hex
function b64tohex(s) {
  var ret = "";
  var i;
  var k = 0; // b64 state, 0-3
  var slop;
  for(i = 0; i < s.length; ++i) {
    if(s.charAt(i) == b64pad) break;
    v = b64map.indexOf(s.charAt(i));
    if(v < 0) continue;
    if(k == 0) {
      ret += int2char(v >> 2);
      slop = v & 3;
      k = 1;
    }
    else if(k == 1) {
      ret += int2char((slop << 2) | (v >> 4));
      slop = v & 0xf;
      k = 2;
    }
    else if(k == 2) {
      ret += int2char(slop);
      ret += int2char(v >> 2);
      slop = v & 3;
      k = 3;
    }
    else {
      ret += int2char((slop << 2) | (v >> 4));
      ret += int2char(v & 0xf);
      k = 0;
    }
  }
  if(k == 1)
    ret += int2char(slop << 2);
  return ret;
}

// convert a base64 string to a byte/number array
function b64toBA(s) {
  //piggyback on b64tohex for now, optimize later
  var h = b64tohex(s);
  var i;
  var a = new Array();
  for(i = 0; 2*i < h.length; ++i) {
    a[i] = parseInt(h.substring(2*i,2*i+2),16);
  }
  return a;
}


/* File: Templates/javascript/encr.js */
var e = "3";
var pubKey = null;
var rsa = null;



function do_encrypt(ptext) {
  
  var res = rsa.encrypt(ptext);
  return res;
}
/*
function do_encrypt_with_form(ptext) {

  var res = do_encrypt(ptext);
  var after = new Date();
  if(res) {
   // alert(ptext);
    var input = document.createElement("input");
    input.type = "hidden";
    input.name = "epass";
    input.id = "epass";
    input.value = res;
    
    var form = document.getElementById("newuserini");
    form.appendChild(input);
    
    
    var after = new Date();
    ptext.value = "";
    return true;
   // alert(before+"\n"+after);
  }
  return false;
}
*/



/* File: Templates/javascript/filter.js */
/*
*
* This software may contain computer code that is proprietary to Interactive Brokers LLC
* and may not be copied, distributed or used for any purpose other than the authorized use
* of this software. Copyright, Interactive Brokers LLC (2008). All rights reserved.
*
*/

function fortifyFilter(input) {
	var output = "";
	var inputLower = input.toLowerCase();
	var index1 = inputLower.indexOf("script");
	var index2 = inputLower.indexOf("java");
	if(index1>0 || index2>0) {
	 	alert("BAD STRING");
		return "XX";
	} else {		 
		for(var p=0; p<input.length; p++){
			var value=getCharIndirectly(input.charAt(p));
	    	if(value =="XX"){
	    		return value;
	    	}
	    	output = output + value;
	    }
	    return output;
   	}
}

function fortifyFilterIncludeSpace(input) {
	var output = "";
	var inputLower = input.toLowerCase();
	var index1 = inputLower.indexOf("script");
	var index2 = inputLower.indexOf("java");
	if(index1>0 || index2>0) {
	 	alert("BAD STRING");
		return "XX";
	} else {		 
		for(var p=0; p<input.length; p++){
	    	output = output + getCharIndirectlyWithSpace(input.charAt(p));
	    }
	    return output;
   	}
}


function fortifyFilterOpenRedirect(inputValue) {
	var output = "";
	if(inputValue==null)
		return null;
		
	var input = ""+inputValue;
	if(input!="") {
		var inputLower = input.toLowerCase();
		var index1 = inputLower.indexOf("www.");
		var index2 = inputLower.indexOf("http:");
		if(index1>0 || index2>0) {
		 	alert("BAD STRING");
			return "XX";
		} else {		 
			for(var p=0; p<input.length; p++){
			var value=getAlphaNumericIndirectly(input.charAt(p));
	    	if(value =="XX"){
	    		return value;
	    	}
	    	output = output + value;
		    }
		    return output;
	   	}
   	} else {
   		return output;
	}
}

function getCharIndex(inputChar) {
 	var allowedCharStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890._";
	var index = allowedCharStr.indexOf(inputChar);
	return index;
}

function getCharIndirectly(inputChar) {
	 var allowedCharStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_./&";
	 var index = allowedCharStr.indexOf(inputChar);
	 if(index<0) {
		 alert("BAD CHARACTER");
		 return "XX";
	 } else {
	  	return allowedCharStr.charAt(index);
	 }
}

function getCharIndirectlyWithSpace(inputChar) {
	 var allowedCharStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890_./& ";
	 var index = allowedCharStr.indexOf(inputChar);
	 if(index<0) {
		 alert("BAD CHARACTER");
		 return "XX";
	 } else {
	  	return allowedCharStr.charAt(index);
	 }
}

function getAlphaNumericIndirectly(inputChar) {
	 var allowedCharStr = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ1234567890@._ -";
	 var index = allowedCharStr.indexOf(inputChar);
	 if(index<0) {
		 alert("BAD CHARACTER");
		 return "XX";
	 } else {
	  	return allowedCharStr.charAt(index);
	 }
}

