
// The SHA-1 code in this section was copied from:
// A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined in FIPS PUB 180-1
// Version 2.1a Copyright Paul Johnston 2000 - 2002.
// Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
// Distributed under the BSD License
// See http://pajhome.org.uk/crypt/md5 for details.

function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * 8));}
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}

// Calculate the SHA-1 of an array of big-endian words, and a bit length

function core_sha1(x, len)
{
  x[len >> 5] |= 0x80 << (24 - len % 32);  // append padding
  x[((len + 64 >> 9) << 4) + 15] = len;

  var w = Array(80);
  var a =  1732584193;
  var b = -271733879;
  var c = -1732584194;
  var d =  271733878;
  var e = -1009589776;

  for (var i = 0; i < x.length; i += 16)
  {
    var olda = a;
    var oldb = b;
    var oldc = c;
    var oldd = d;
    var olde = e;

    for (var j = 0; j < 80; j++)
    {
      if (j < 16)
      {
        w[j] = x[i + j];
      }
      else
      {
        w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
      }
      
      var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
      safe_add(safe_add(e, w[j]), sha1_kt(j)));
      e = d;
      d = c;
      c = rol(b, 30);
      b = a;
      a = t;
    }

    a = safe_add(a, olda);
    b = safe_add(b, oldb);
    c = safe_add(c, oldc);
    d = safe_add(d, oldd);
    e = safe_add(e, olde);
  }
  
  return Array(a, b, c, d, e);
}

// Perform the appropriate triplet combination function for the current iteration

function sha1_ft(t, b, c, d)
{
  if (t < 20)
  {
    return (b & c) | ((~b) & d);
  }

  if (t < 40)
  {
    return b ^ c ^ d;
  }

  if (t < 60)
  {
    return (b & c) | (b & d) | (c & d);
  }
  
  return b ^ c ^ d;
}

// Determine the appropriate additive constant for the current iteration

function sha1_kt(t)
{
  return (t < 20) ? 1518500249
                  : (t < 40) ? 1859775393
                             : (t < 60) ? -1894007588
                                        : -899497514;
}

// Calculate the HMAC-SHA1 of a key and some data

function core_hmac_sha1(key, data)
{
  var bkey = str2binb(key);
  if (bkey.length > 16)
  {
    bkey = core_sha1(bkey, key.length * 8);
  }

  var ipad = Array(16), opad = Array(16);
  for (var i = 0; i < 16; i++)
  {
    ipad[i] = bkey[i] ^ 0x36363636;
    opad[i] = bkey[i] ^ 0x5C5C5C5C;
  }

  var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * 8);
  return core_sha1(opad.concat(hash), 512 + 160);
}

// Add integers, wrapping at 2^32. This uses 16-bit operations internally
// to work around bugs in some JS interpreters.

function safe_add(x, y)
{
  var lsw = (x & 0xFFFF) + (y & 0xFFFF);
  var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
  return (msw << 16) | (lsw & 0xFFFF);
}

// Bitwise rotate a 32-bit number to the left.

function rol(num, cnt)
{
  return (num << cnt) | (num >>> (32 - cnt));
}


// Convert an 8-bit string to an array of big-endian words
// Characters > 255 have their hi-byte silently ignored.

function str2binb(str)
{
  var bin = Array();
  var mask = (1 << 8) - 1;
  for (var i = 0; i < str.length * 8; i += 8)
  {
    bin[i>>5] |= (str.charCodeAt(i / 8) & mask) << (32 - 8 - i%32);
  }
  
  return bin;
}

// Convert an array of big-endian words to a string

function binb2str(bin)
{
  var str = "";
  var mask = (1 << 8) - 1;
  for (var i = 0; i < bin.length * 32; i += 8)
  {
    str += String.fromCharCode((bin[i>>5] >>> (32 - 8 - i%32)) & mask);
  }
  
  return str;
}

// Convert an array of big-endian words to a base-64 string

function binb2b64(binarray)
{
  var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
  var str = "";
  for (var i = 0; i < binarray.length * 4; i += 3)
  {
    var triplet = (((binarray[i   >> 2] >> 8 * (3 -  i   %4)) & 0xFF) << 16)
                  | (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
                  |  ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
    
    for (var j = 0; j < 4; j++)
    {
      if (i * 8 + j * 6 > binarray.length * 32)
      {
        str += '=';
      }
      else
      {
        str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
      }
    }
  }
  
  return str;
}

function onNewParam(current)
{
  document.getElementById('paramAdd' + current).style.visibility = 'hidden';

  if (current > 1)
  {
    document.getElementById('paramDel' + current).style.visibility = 'hidden';
  }

  var holder = document.getElementById('paramHolder');
  holder.id = 'paramHolder' + current;
  var newContent = '<span id="param' + (current + 1) + '">' +
                    '<label>Name:' + '<input id="paramName' + (current + 1) + '" type="text" onkeyup="onParamNameChange(' + (current + 1) + ');" />' + '</label>' +
                    '<span id="paramSub' + (current + 1) + '" style="visibility:hidden">' +
                    '<label>Value:' + '<input id="paramValue' + (current + 1) + '" type="text" onkeyup="setContent();" />' + '</label>';

  if (current < 10)
  {				
    newContent += '<input id="paramAdd' + (current + 1) + '" type="button" value="+" onclick="onNewParam(' + (current + 1) + ');" />';
  }

  newContent += '</span>' + '<input id="paramDel' + (current + 1) + '" type="button" value="-" onclick="onDelParam(' + (current + 1) + ');" />' + '</span><br />' + '<span id="paramHolder" />';
  holder.innerHTML = newContent;
}

function onDelParam(current)
{
  document.getElementById('paramAdd' + (current - 1)).style.visibility = 'inherit';

  if (current > 2)
  {
    document.getElementById('paramDel' + (current - 1)).style.visibility = 'visible';
  }

  var holder = document.getElementById('paramHolder' + (current - 1));
  if (holder != null)
  {
    holder.innerHTML = '';
    holder.id = 'paramHolder';
  }
  
  setContent();
}

function onParamNameChange(current)
{
  var name = document.getElementById('paramName' + current).value;
  if (name != '')
  {
    document.getElementById('paramSub' + current).style.visibility = 'visible';
  }
  else
  {
    document.getElementById('paramSub' + current).style.visibility = 'hidden';
  }

  setContent();
}

function onUriSchemeChange(scheme)
{
  switch(scheme)
  {
  case 'http':
    {
      document.getElementById('port').value = '80';
      break;
    }
  case 'https':
    {
      document.getElementById('port').value = '443';
      break;
    }
  }

  setContent();
}

function onSignatureMethodChange(index)
{
  switch(index)
  {
  case 0:
  case 2:
    {
      document.getElementById('pkBlock').style.display = 'none';
      break;
    }
  case 1:
    {
      document.getElementById('pkBlock').style.display = '';
      if (document.RSA.checkKey(document.getElementById('privateKey').value.replace(/[\t\n\r ]/g, '')))
      {
      	document.getElementById('privateKey').style.color = 'green';
      }
      else
      {
      	document.getElementById('privateKey').style.color = 'red';
      }
      
      break;
    }
  }

  setContent();
}

function onPrivateKeyChange(value)
{
  if (document.RSA.checkKey(value.replace(/[\t\n\r ]/g, '')))
  {
  	document.getElementById('privateKey').style.color = 'green';
  }
  else
  {
  	document.getElementById('privateKey').style.color = 'red';
  }
  
  setContent();
}

function setExample(type)
{
  if (type == 0)
  {
    var exm = document.exampleControl.exampleValues;
    exm[1].checked = false;
    exm[2].checked = false;
    exm[3].checked = false;
    exm[0].checked = true;

    type = 1;
  }
  
  document.getElementById('privateKey').value = 'MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBALRiMLAh9iimur8V\n' +
                                                'A7qVvdqxevEuUkW4K+2KdMXmnQbG9Aa7k7eBjK1S+0LYmVjPKlJGNXHDGuy5Fw/d\n' +
                                                '7rjVJ0BLB+ubPK8iA/Tw3hLQgXMRRGRXXCn8ikfuQfjUS1uZSatdLB81mydBETlJ\n' +
                                                'hI6GH4twrbDJCR2Bwy/XWXgqgGRzAgMBAAECgYBYWVtleUzavkbrPjy0T5FMou8H\n' +
                                                'X9u2AC2ry8vD/l7cqedtwMPp9k7TubgNFo+NGvKsl2ynyprOZR1xjQ7WgrgVB+mm\n' +
                                                'uScOM/5HVceFuGRDhYTCObE+y1kxRloNYXnx3ei1zbeYLPCHdhxRYW7T0qcynNmw\n' +
                                                'rn05/KO2RLjgQNalsQJBANeA3Q4Nugqy4QBUCEC09SqylT2K9FrrItqL2QKc9v0Z\n' +
                                                'zO2uwllCbg0dwpVuYPYXYvikNHHg+aCWF+VXsb9rpPsCQQDWR9TT4ORdzoj+Nccn\n' +
                                                'qkMsDmzt0EfNaAOwHOmVJ2RVBspPcxt5iN4HI7HNeG6U5YsFBb+/GZbgfBT3kpNG\n' +
                                                'WPTpAkBI+gFhjfJvRw38n3g/+UeAkwMI2TJQS4n8+hid0uus3/zOjDySH3XHCUno\n' +
                                                'cn1xOJAyZODBo47E+67R4jV1/gzbAkEAklJaspRPXP877NssM5nAZMU0/O/NGCZ+\n' +
                                                '3jPgDUno6WbJn5cqm8MqWhW1xGkImgRk+fkDBquiq4gPiT898jusgQJAd5Zrr6Q8\n' +
                                                'AO/0isr/3aa6O6NLQxISLKcPDk2NOccAfS/xOtfOz4sJYM3+Bs4Io9+dZGSDCA54\n' +
                                                'Lw03eHTNQghS0A==';

  onDelParam(2);
  document.getElementById('paramValue1').value = '';
  document.getElementById('paramName1').value = '';
  onParamNameChange(1);
  
  switch (type)
  {
  case 1:
    {
      document.getElementById('uriScheme')[0].selected = true;
      document.getElementById('httpMethod')[0].selected = true;
      document.getElementById('hostName').value = 'photos.example.net';
      document.getElementById('port').value = '80';
      document.getElementById('requestPath').value = '/photos';

      document.getElementById('paramName1').value = 'size';
      onParamNameChange(1);
      document.getElementById('paramValue1').value = 'original';
      onNewParam(1);
      document.getElementById('paramName2').value = 'file';
      onParamNameChange(2);
      document.getElementById('paramValue2').value = 'vacation.jpg';

      document.getElementById('consumerKey').value = 'dpf43f3p2l4k3l03';
      document.getElementById('consumerSecret').value = 'kd94hf93k423kf44';
      document.getElementById('token').value = 'nnch734d00sl2jdk';
      document.getElementById('tokenSecret').value = 'pfkkdhi9sl3r4s00';
      document.getElementById('signatureMethod')[0].selected = true;

      document.getElementById('timestamp').value = '1191242096';
      syncDateFromTimestamp();
      document.getElementById('nonce').value = 'kllo9940pd9333jh';
      document.getElementById('version')[0].selected = true;
      break;
    }
  case 2:
    {
      document.getElementById('uriScheme')[0].selected = true;
      document.getElementById('httpMethod')[0].selected = true;
      document.getElementById('hostName').value = 'PHOTOS.example.net';
      document.getElementById('port').value = '8001';
      document.getElementById('requestPath').value = '/Photos';

      document.getElementById('paramName1').value = 'photo size';
      onParamNameChange(1);
      document.getElementById('paramValue1').value = '300%';
      onNewParam(1);
      document.getElementById('paramName2').value = 'title';
      onParamNameChange(2);
      document.getElementById('paramValue2').value = 'Back of $100 Dollars Bill';

      document.getElementById('consumerKey').value = 'dpf43f3++p+#2l4k3l03';
      document.getElementById('consumerSecret').value = 'kd9@4h%%4f93k423kf44';
      document.getElementById('token').value = 'nnch734d(0)0sl2jdk';
      document.getElementById('tokenSecret').value = 'pfkkd#hi9_sl-3r=4s00';
      document.getElementById('signatureMethod')[0].selected = true;

      document.getElementById('timestamp').value = '1191242096';
      syncDateFromTimestamp();
      document.getElementById('nonce').value = 'kllo~9940~pd9333jh';
      document.getElementById('version')[0].selected = true;
      break;
    }
  case 3:
    {
      document.getElementById('uriScheme')[0].selected = true;
      document.getElementById('httpMethod')[0].selected = true;
      document.getElementById('hostName').value = 'PHOTOS.example.net';
      document.getElementById('port').value = '8001';
      document.getElementById('requestPath').value = '/Photos';

      document.getElementById('paramName1').value = 'type';
      onParamNameChange(1);
      document.getElementById('paramValue1').value = 'אוטובוס';
      onNewParam(1);
      document.getElementById('paramName2').value = 'scenario';
      onParamNameChange(2);
      document.getElementById('paramValue2').value = 'תאונה';

      document.getElementById('consumerKey').value = 'dpf43f3++p+#2l4k3l03';
      document.getElementById('consumerSecret').value = 'kd9@4h%%4f93k423kf44';
      document.getElementById('token').value = 'nnch734d(0)0sl2jdk';
      document.getElementById('tokenSecret').value = 'pfkkd#hi9_sl-3r=4s00';
      document.getElementById('signatureMethod')[0].selected = true;

      document.getElementById('timestamp').value = '1191242096';
      syncDateFromTimestamp();
      document.getElementById('nonce').value = 'kllo~9940~pd9333jh';
      document.getElementById('version')[0].selected = true;
      break;
    }
  case 4:
    {
      document.getElementById('uriScheme')[0].selected = true;
      document.getElementById('httpMethod')[0].selected = true;
      document.getElementById('hostName').value = '';
      document.getElementById('port').value = '80';
      document.getElementById('requestPath').value = '/';

      document.getElementById('paramValue1').value = '';
      onParamNameChange(1);
      document.getElementById('paramName1').value = '';

      document.getElementById('consumerKey').value = '';
      document.getElementById('consumerSecret').value = '';
      document.getElementById('token').value = '';
      document.getElementById('tokenSecret').value = '';
      document.getElementById('signatureMethod')[0].selected = true;
      document.getElementById('privateKey').value = '';

      document.getElementById('year')[0].selected = true;
      document.getElementById('month')[0].selected = true;
      document.getElementById('day')[0].selected = true;
      document.getElementById('hour')[0].selected = true;
      document.getElementById('minute')[0].selected = true;
      document.getElementById('second')[0].selected = true;
      document.getElementById('timestamp').value = '0';
      document.getElementById('nonce').value = '';
      document.getElementById('version')[0].selected = true;

      collapseExpandLink('httpRequestBlock', 1);
      collapseExpandLink('credentialsBlock', 1);
      collapseExpandLink('sigBlock', 1);
      collapseExpandLink('tsBlock', 1);
      break;
    }
  }

  onSignatureMethodChange(0);
  setContent();
}

function syncTimestampFromDate()
{
  var date = new Date(Date.UTC(document.getElementById('year').value, (document.getElementById('month').value - 1),
  document.getElementById('day').value, document.getElementById('hour').value,
  document.getElementById('minute').value, document.getElementById('second').value));
  document.getElementById('timestamp').value = (date.getTime() / 1000.0);

  setContent();
}

function syncDateFromTimestamp()
{
  if (document.getElementById('timestamp').value > 1609459199)
  {
    document.getElementById('timestamp').value = 1609459199;
  }

  var date = new Date(document.getElementById('timestamp').value * 1000);
  document.getElementById('year')[date.getUTCFullYear() - 1970].selected = true;
  document.getElementById('month')[date.getUTCMonth()].selected = true;
  document.getElementById('day')[date.getUTCDate() - 1].selected = true;
  document.getElementById('hour')[date.getUTCHours()].selected = true;
  document.getElementById('minute')[date.getUTCMinutes()].selected = true;
  document.getElementById('second')[date.getUTCSeconds()].selected = true;

  setContent();
}

function utf8Encode(string)
{
  string = string.replace(/\r\n/g,'\n');
  var utfString = '';

  for (var i = 0 ; i < string.length ; i++)
  {
    var chr = string.charCodeAt(i);
    if (chr < 128)
    {
      utfString += String.fromCharCode(chr);
    }
    else if ((chr > 127) && (chr < 2048))
    {
      utfString += String.fromCharCode((chr >> 6) | 192);
      utfString += String.fromCharCode((chr & 63) | 128);
    }
    else
    {
      utfString += String.fromCharCode((chr >> 12) | 224);
      utfString += String.fromCharCode(((chr >> 6) & 63) | 128);
      utfString += String.fromCharCode((chr & 63) | 128);
    }
  }

  return utfString;
}

function urlEncode(string)
{
  var urlString = '';

  for (var i = 0 ; i < string.length ; i++)
  {
    var chr = string.charCodeAt(i);

    if ((chr >= 48 && chr <= 57) || // 09
        (chr >= 65 && chr <= 90) || // AZ
        (chr >= 97 && chr <= 122) || // az
        chr == 45 || // -
        chr == 95 || // _
        chr == 46 || // .
        chr == 126) // ~
    {
      urlString += String.fromCharCode(chr);
    }
    else
    {
      urlString += '%' + chr.toString(16).toUpperCase();
    }
  }

  return urlString;
}

var sig = '';

function setContent()
{
  function Parameter(name,value)
  {
    this.name = name;
    this.value = value;
  }

  var oauthParams = new Array();

  oauthParams.push(new Parameter('oauth_consumer_key', document.getElementById('consumerKey').value));
  oauthParams.push(new Parameter('oauth_token', document.getElementById('token').value));
  oauthParams.push(new Parameter('oauth_nonce', document.getElementById('nonce').value));
  oauthParams.push(new Parameter('oauth_timestamp', document.getElementById('timestamp').value));
  oauthParams.push(new Parameter('oauth_signature_method', document.getElementById('signatureMethod')[document.getElementById('signatureMethod').selectedIndex].innerHTML));

  if (document.getElementById('version')[0].selected == true)
  {
    oauthParams.push(new Parameter('oauth_version', '1.0'));
  }

  // Set OAuth Parameters Table

  var oauthParamsString = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody>';

  var isOdd = false;
  for (var param in oauthParams)
  {
    oauthParamsString += '<tr';

    if (isOdd)
    {
      oauthParamsString += ' class="odd"';
    }

    isOdd = !isOdd;

    oauthParamsString += '><td>' + oauthParams[param].name + '</td><td>' + oauthParams[param].value + '</tr></tr>';
  }

  oauthParamsString += '</tbody></table>';
  document.getElementById('oauthParams').innerHTML = oauthParamsString;

  // Inline Parameters

  var localParams = new Array();
  var iParams = '';
  for (var i = 1 ; ; ++i)
  {
    var element = document.getElementById('paramName' + i);
    if (element == null ||
        element.value.length == 0)
    {
      break;
    }

    var name = element.value;
    var value = document.getElementById('paramValue' + i).value;

    if (i != 1)
    {
      iParams += ', ';
    }

    iParams += name + '=' + value;
    localParams.push(new Parameter(name, value));
  }

  if (iParams.length == 0)
  {
    document.getElementById('iParamsPrefix').innerHTML = 'without any parameters';
  }
  else
  {
    document.getElementById('iParamsPrefix').innerHTML = 'with the parameters ';
  }

  document.getElementById('iParams').innerHTML = iParams;

  // Inline URL

  var port = document.getElementById('port').value;
  var scheme = document.getElementById('uriScheme')[document.getElementById('uriScheme').selectedIndex].innerHTML;

  var URLString = scheme + '://' + document.getElementById('hostName').value;

  if ((port != 80 || scheme != 'http') && (port != 443 || scheme != 'https'))
  {
    URLString += ':' + port;
  }

  if (document.getElementById('requestPath').value.charCodeAt(0) != 47)
  {
    URLString += '/';
  }

  URLString += document.getElementById('requestPath').value;
  document.getElementById('iLocation').innerHTML = URLString;

  // Fixed values

  document.getElementById('iKey').innerHTML = document.getElementById('consumerKey').value;
  document.getElementById('iCSecret').innerHTML = document.getElementById('consumerSecret').value;
  document.getElementById('iToken').innerHTML = document.getElementById('token').value;
  document.getElementById('iTSecret').innerHTML = document.getElementById('tokenSecret').value;
  document.getElementById('iTimestamp').innerHTML = document.getElementById('timestamp').value;
  document.getElementById('iNonce').innerHTML = document.getElementById('nonce').value;
  document.getElementById('iMethod').innerHTML = document.getElementById('signatureMethod')[document.getElementById('signatureMethod').selectedIndex].innerHTML;

  // Signature	

  if (document.getElementById('signatureMethod').selectedIndex != 2)
  {
    document.getElementById('sbsContent').style.display = '';
    document.getElementById('plaintextContent').style.display = 'none';

    setSignedContent();
  }
  else
  {
    // PLAINTEXT

    document.getElementById('sbsContent').style.display = 'none';
    document.getElementById('hmacContent').style.display = 'none';
    document.getElementById('rsaContent').style.display = 'none';
    document.getElementById('plaintextContent').style.display = '';

    sig = urlEncode(utf8Encode(document.getElementById('consumerSecret').value)) + '&' + urlEncode(utf8Encode(document.getElementById('tokenSecret').value));
    document.getElementById('signature').value = sig;
  }

  // Add signature to oauth parameters

  oauthParams.push(new Parameter('oauth_signature', sig));
  
  document.getElementById('sigParam').innerHTML = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody><tr><td>oauth_signature</td><td>' + sig + '</td></tr></tbody></table>';

  // Final output

  var final = document.getElementById('httpMethod')[document.getElementById('httpMethod').selectedIndex].innerHTML + ' ';
  if (document.getElementById('requestPath').value.charCodeAt(0) != 47)
  {
    final += '/';
  }

  final += document.getElementById('requestPath').value;

  for (var p in localParams)
  {
    if (p == 0)
    {
      final += '?';
    }
    else
    {
      final += '&';
    }

    final += escape(localParams[p].name) + '=' + escape(localParams[p].value);
  }

  final += ' HTTP/1.1\n' +
            'Host: ' + document.getElementById('hostName').value.toLowerCase() + ':' + port + '\n' +
            'Authorization: OAuth realm="' + URLString + '"';

  for (var param in oauthParams)
  {
    final += ',\n    ' + oauthParams[param].name + '="' + urlEncode(utf8Encode(oauthParams[param].value)) + '"';
  }

  document.getElementById('final').value = final;
}

function setSignedContent()
{
  // Collect Raw Parameters

  function Parameter(name,value)
  {
    this.name = name;
    this.value = value;
  }

  var localParams = new Array();

  localParams.push(new Parameter('oauth_consumer_key', document.getElementById('consumerKey').value));
  localParams.push(new Parameter('oauth_token', document.getElementById('token').value));
  localParams.push(new Parameter('oauth_nonce', document.getElementById('nonce').value));
  localParams.push(new Parameter('oauth_timestamp', document.getElementById('timestamp').value));
  localParams.push(new Parameter('oauth_signature_method', document.getElementById('signatureMethod')[document.getElementById('signatureMethod').selectedIndex].innerHTML));

  if (document.getElementById('version')[0].selected == true)
  {
    localParams.push(new Parameter('oauth_version', '1.0'));
  }

  // Build full parameters list

  for (var i = 1 ; ; ++i)
  {
    var element = document.getElementById('paramName' + i);
    if (element == null ||
        element.value.length == 0)
    {
      break;
    }

    var name = element.value;
    var value = document.getElementById('paramValue' + i).value;

    var newParam = new Parameter(name, value);
    localParams.push(newParam);
  }

  var rawParams = new Array();
  var sortedParams = new Array();

  for (var param in localParams)
  {
    var newParam = new Parameter(localParams[param].name, localParams[param].value);
    newParam.nameUTF8 = utf8Encode(localParams[param].name);
    newParam.valueUTF8 = utf8Encode(localParams[param].value);
    newParam.nameURL= urlEncode(utf8Encode(localParams[param].name));
    newParam.valueURL = urlEncode(utf8Encode(localParams[param].value));

    rawParams.push(newParam);
    sortedParams.push(newParam);
  }

  sortedParams.sort(function(a,b) { if (a.nameURL < b.nameURL) return -1;
                                    if (a.nameURL > b.nameURL) return 1;
                                    if (a.valueURL < b.valueURL) return  -1;
                                    if (a.valueURL > b.valueURL) return 1;
                                    return 0; });

  // Set Raw Parameters Table

  var rawParamsString = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody>';

  var isOdd = false;
  for (var param in rawParams)
  {
    rawParamsString += '<tr';

    if (isOdd)
    {
      rawParamsString += ' class="odd"';
    }

    isOdd = !isOdd;

    rawParamsString += '><td>' + rawParams[param].name + '</td><td>' + rawParams[param].value + '</tr></tr>';
  }

  rawParamsString += '</tbody></table>';
  document.getElementById('allRawParams').innerHTML = rawParamsString;

  // Set UTF-8 Encoded Table

  var utf8EncodedParamsString = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody>';

  isOdd = false;
  for (var param in rawParams)
  {
    utf8EncodedParamsString += '<tr'

    if (isOdd)
    {
      utf8EncodedParamsString += ' class="odd"';
    }

    isOdd = !isOdd;

    utf8EncodedParamsString += '><td>' + rawParams[param].nameUTF8 + '</td><td>' + rawParams[param].valueUTF8 + '</tr></tr>';
  }

  utf8EncodedParamsString += '</tbody></table>';
  document.getElementById('utf8EncodedParams').innerHTML = utf8EncodedParamsString;

  // Set URL Encoded Table

  var urlEncodedParamsString = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody>';

  isOdd = false;
  for (var param in rawParams)
  {
    urlEncodedParamsString += '<tr';

    if (isOdd)
    {
      urlEncodedParamsString += ' class="odd"';
    }

    isOdd = !isOdd;

    urlEncodedParamsString += '><td>' + rawParams[param].nameURL + '</td><td>' + rawParams[param].valueURL + '</tr></tr>';
  }

  urlEncodedParamsString += '</tbody></table>';
  document.getElementById('urlEncodedParams').innerHTML = urlEncodedParamsString;

  // Set Sorted Table

  var sortedParamsString = '<table align="center"><thead><tr><th>Name</th><th>Value</th></td></thead><tbody>';

  isOdd = false;
  for (var param in sortedParams)
  {
    sortedParamsString += '<tr';

    if (isOdd)
    {
      sortedParamsString += ' class="odd"';
    }

    isOdd = !isOdd;
    sortedParamsString += '><td>' + sortedParams[param].nameURL + '</td><td>' + sortedParams[param].valueURL + '</tr></tr>';
  }

  sortedParamsString += '</tbody></table>';
  document.getElementById('sortedParams').innerHTML = sortedParamsString;

  // Set Concatenated Parameters

  var concatParamsString = '';

  for (var i = 0 ; i < sortedParams.length ; ++i)
  {
    if (i != 0)
    {
      concatParamsString += '&';
    }

    concatParamsString += sortedParams[i].nameURL + '=' + sortedParams[i].valueURL;
  }

  document.getElementById('concatParams').value = concatParamsString;

  // Prepare URL

  var port = document.getElementById('port').value;
  var scheme = document.getElementById('uriScheme')[document.getElementById('uriScheme').selectedIndex].innerHTML;
  var fullURLString = scheme + '://' + document.getElementById('hostName').value + ':' + port;

  var requestPath = document.getElementById('requestPath').value;
  if (requestPath.charCodeAt(0) != 47)
  {
    fullURLString += '/';
  }

  fullURLString += requestPath ;

  var normURLString = scheme + '://' + document.getElementById('hostName').value;

  if ((port != 80 || scheme != 'http') && (port != 443 || scheme != 'https'))
  {
    normURLString += ':' + port;
  }

  if (requestPath.charCodeAt(0) != 47)
  {
    normURLString += '/';
  }

  normURLString = normURLString.toLowerCase() + requestPath;
  document.getElementById('prepURL').innerHTML = '<table align="center"><tbody><tr><td>Full URL</td><td>' + fullURLString + '</td></tr><tr class="odd"><td>Normalized URL</td><td>' + normURLString + '</td></tr></tbody></table>';;

  // Base String

  var baseString = document.getElementById('httpMethod')[document.getElementById('httpMethod').selectedIndex].innerHTML + '&' + urlEncode(normURLString) + '&' + urlEncode(concatParamsString);
  document.getElementById('baseString').value = baseString;

  if (document.getElementById('signatureMethod')[0].selected == true)
  {
    // HMAC-SHA1

    document.getElementById('hmacContent').style.display = '';
    document.getElementById('rsaContent').style.display = 'none';

    // Build Hash Key

    var hashKey = urlEncode(utf8Encode(document.getElementById('consumerSecret').value)) + '&' + urlEncode(utf8Encode(document.getElementById('tokenSecret').value));
    document.getElementById('hashKey').value = hashKey;

    // Signature

    sig = b64_hmac_sha1(hashKey, baseString);
    document.getElementById('signature').value = sig;
  }
  else
  {
    // RSA-SHA1

    document.getElementById('rsaContent').style.display = '';
    document.getElementById('hmacContent').style.display = 'none';

    // Signature

    sig = document.RSA.sign(baseString, document.getElementById('privateKey').value.replace(/[\t\n\r ]/g, ''));
    document.getElementById('signature').value = sig;
  }
}

function collapseExpandLink(elementName, expand)
{
  var element = document.getElementById(elementName);
  var elementX = document.getElementById(elementName + 'X');

  if ((element.style.display == '' && expand == 2) || expand == 0)
  {
    element.style.display = 'none';
    elementX.innerHTML = '&nbsp;+&nbsp;';
  }
  else
  {
    element.style.display = '';
    elementX.innerHTML = '&nbsp;-&nbsp;';
  }
}
