var ses = ses ? ses : {};
ses.utils = ses.utils ? ses.utils : {};

/**
 * This base64 code is derived from code written by Tyler Akins and placed
 * in the public domain at http://rumkin.com
 */

ses.utils.Base64 =
{
  _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=",

  encode: function( input )
  {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
   
    // bug 6168814: utf-8 encode first
    input = ses.utils.Base64.encode_utf8(input);
    
    do
    {
      chr1 = input.charCodeAt( i++ );
      chr2 = input.charCodeAt( i++ );
      chr3 = input.charCodeAt( i++ );
      
      enc1 = chr1 >> 2;
      enc2 = ( ( chr1 & 3 ) << 4 ) | ( chr2 >> 4 );
      enc3 = ( ( chr2 & 15 ) << 2 ) | ( chr3 >> 6 );
      enc4 = chr3 & 63;
      
      if( isNaN( chr2 ) )
      {
        enc3 = enc4 = 64;
      }
      else if( isNaN( chr3 ) )
      {
        enc4 = 64;
      }
      
      output = output + this._keyStr.charAt( enc1 ) + this._keyStr.charAt( enc2 ) + 
      this._keyStr.charAt( enc3 ) + this._keyStr.charAt( enc4 );
    } while( i < input.length );
    
    return output;
  },
  
  decode : function( input )
  {
    var output = "";
    var chr1, chr2, chr3;
    var enc1, enc2, enc3, enc4;
    var i = 0;
  
    // remove all characters that are not A-Z, a-z, 0-9, +, /, or =
    input = input.replace( /[^A-Za-z0-9\+\/\=]/g, "" );
  
    do
    {
      enc1 = this._keyStr.indexOf( input.charAt( i++ ) );
      enc2 = this._keyStr.indexOf( input.charAt( i++ ) );
      enc3 = this._keyStr.indexOf( input.charAt( i++ ) );
      enc4 = this._keyStr.indexOf( input.charAt( i++ ) );
      
      chr1 = ( enc1 << 2 ) | ( enc2 >> 4 );
      chr2 = ( ( enc2 & 15 ) << 4 ) | ( enc3 >> 2 );
      chr3 = ( ( enc3 & 3 ) << 6 ) | enc4;
      
      output = output + String.fromCharCode( chr1 );
      
      if( enc3 != 64 )
      {
        output = output + String.fromCharCode( chr2 );
      }
      if ( enc4 != 64 )
      {
        output = output + String.fromCharCode( chr3 );
      }
    } while( i < input.length );
  
    // bug 6168814: utf-8 decode
    output = ses.utils.Base64.decode_utf8(output);

    return output;
  },

  /** 
   * The following UTF-8 encoder/decoder functions will work with all characters
   * inside the Basic Multilingual Plane (BMP).
   * -- gb
   */
   
  /**
   * decode_utf8: decode a string from UTF-8 to UCS-4.
   */
  decode_utf8 : function (utf8str) 
  {
    var decoded = "";
    var i = c1 = c2 = c3 = 0;
    while (i < utf8str.length)
    {
      c1 = utf8str.charCodeAt(i);
      if (c1 < 128)
      {
        // 1 byte: unicode = c1
        decoded += String.fromCharCode(c1);
        i++;
      }
      else if (c1 > 191 && c1 < 225)
      {
        c2 = utf8str.charCodeAt(i + 1); // c2 <= 191
        
        /**
         * There are 2 bytes, so unicode is (c1 - 192)*64 + (c2 - 128)
         *
         * Identities: 
         * 1. (x - 192) = (x mod 32) = (x & 31), where 192 <= x <= 224
         * 2. (x - 128) = (x - 2*64) = (x mod 64) = (x & 63), where x <= 191
         */
        decoded += String.fromCharCode(((c1 & 31) << 6) | (c2 & 63));
        i += 2;
      }
      else 
      {
        // We can assume that 224 <= c1 <= 239, otherwise we encountered
        // non-BMP characters (unrealistic).
        c2 = utf8str.charCodeAt(i + 1); // c2 <= 191
        c3 = utf8str.charCodeAt(i + 2); // c3 <= 191
        
        /**
         * There are 3 bytes, so unicode is
         * (c1 - 224)*4096 + (c2 - 128)*64 + (c3 - 128)
         *
         * Identities:
         * 1. (x - 224) = (x mod 16) = (x & 15), 224 <= x <= 239
         * 2. (x - 128) = (x mod 64) = (x & 63), x <= 191
         */
        decoded += String.fromCharCode(
          ((c1 & 15) << 12) | ((c2 & 63) << 6) | (c3 & 63)
        );
        i += 3;
      }
    }

    return decoded;
  },

  /**
   * encode_utf8: encode a UCS-4 string in UTF-8.
   */
  encode_utf8 : function (str) 
  {
    str = str.replace(/\r\n/g,"\n");
    var encoded = "";
    var c = 0;
    for (var i = 0; i < string.length; i++) 
    {
      c = str.charCodeAt(i);
      if (c < 128) 
      {
        // 1 byte: utf-8 = c
        encoded += String.fromCharCode(c);
      }
      else if (c < 2048) 
      {
        // utf-8 byte 1 = (c div 64) + 192 = (c >> 6) | 192
        // utf-8 byte 2 = (c mod 64) + 128 = (c & 63) | 128
        encoded += String.fromCharCode((c >> 6) | 192);
        encoded += String.fromCharCode((c & 63) | 128);
      }
      else 
      {
        // We can assume c <= 65535, i.e., that UTF-8 char uses 3 bytes, 
        // otherwise we encountered non-BMP characters (unrealistic).
        
        // utf-8 byte 1 = (c div 4096) + 224 = (c >> 12) | 224
        // utf-8 byte 2 = ((c div 64) mod 64) + 128 = ((c >>> 6) & 63) | 128
        // utf-8 byte 3 = (c mod 64) + 128 = (c & 63) | 128
        encoded += String.fromCharCode((c >> 12) | 224);
        encoded += String.fromCharCode(((c >> 6) & 63) | 128);
        encoded += String.fromCharCode((c & 63) | 128);
      }
    }
    
    return encoded;
  }
};

/**
 * Initialize DOMParser for Internet Explorer
 *
 * This object is used for taking a plain text string containing XML content
 * and turning it into a DOM tree.
 */
if( typeof DOMParser == "undefined" )
{
  DOMParser = function() {}
  DOMParser.prototype.parseFromString = function( str, contentType )
  {
    if( typeof ActiveXObject != "undefined" )
    {
      var d = new ActiveXObject( "MSXML.DomDocument" );
      d.loadXML( str );
      return d;
    }
    else if( typeof XMLHttpRequest != "undefined" )
    {
      var req = new XMLHttpRequest;
      req.open( "GET", "data:" + ( contentType || "application/xml" ) + 
                                 ";charset=utf-8," + encodeURIComponent( str ),
                                 false );
      if( req.overrideMimeType )
      {
        req.overrideMimeType( contentType );
      }
      req.send( null );
      return req.responseXML;
    }
  }
}

/**
 * $() convenience function
 *
 * Returns the DOM element representing a tag with the specified ID.
 *
 * Use this function to replace the following invocation:
 *  document.getElementById( "myid" )
 * with:
 *  $( "myid" )
 */
function $()
{
    var elements = new Array();

    for( var i=0; i<arguments.length; i++ )
    {
      var element = arguments[i];
      if( typeof element == 'string' )
      {
        if( document.getElementById )
        {
          element = document.getElementById( element );
        }
        else if( document.all )
        {
          element = document.all[element];
        }
      }
      elements.push( element );
    }

    if( ( arguments.length == 1 ) && ( elements.length > 0 ) )
    {
      return elements[0];
    }
    else
    {
      return elements;
    }
}

/**
 * $C() convenience function
 *
 * Creates a DOM element for a specific element type.
 *
 * Use this function to replace, for example, the following invocation:
 *  document.createElement( "tr" )
 * with:
 *  $C( "tr" )
 */
function $C( elType )
{
  return document.createElement( elType );
}

/**
 * $T() convenience function
 *
 * Creates a DOM text node.
 *
 * Use this function to replace, for example, the following invocation:
 *  document.createTextNode( "some text" )
 * with:
 *  $T( "some text" )
 */
function $T( txt )
{
  return document.createTextNode( txt );
}
