///////////////////////////////////////////////////////////////////////////////
// maachang.js
// Copyright (c) 2007 masahito suzuki, Inc. All Rights Reserved.
///////////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////////////
// 基本系.
////////////////////////////////////////////////////////////////////////////////

// ブラウザ判別.
var isMSIE = /*@cc_on!@*/false; 
var isSAFARI = navigator.userAgent.indexOf( "Safari" ) != -1 ;
var isOPERA = navigator.userAgent.indexOf( "Opera" ) != -1 ;
var isMOZILLA = navigator.userAgent.indexOf( "Mozilla/" ) != -1 && isMSIE == false ;
var isNETSCAPE = navigator.userAgent.indexOf( "Netscape" ) != -1 ;

// クッキー判別.
var isCookie = navigator.cookieEnabled ;

// ID情報取得.
function $(e){
    if( typeof( e ) == 'string' ) {
        return document.getElementById( e ) ;
    }
    return e ;
} ;

// デバッグ出力.
dbg = function( str ) {
    if( isMOZILLA == true ) {
        console.debug( str ) ;
    }
} ;

// bind.
Function.prototype.bind = function(object) {
    var __method = this;
    return function() {
        return __method.apply(object, arguments);
    }
} ;

// データ判別.
isNull = function( value ) {
    if( value == null || typeof( value ) == "undefined" ) {
        return true ;
    }
    return false ;
} ;

// 文字列が存在するかチェック.
useString = function(str){
    if( isNull( str ) == true || trim( str ).length <= 0 ) {
        return false ;
    }
    return true ;
} ;

// 数値チェック.
isNumeric = function(num){
    if (num.match(/[^0-9]/g)) {
        return false;
    }
    return true;
}

// スペースを削除.
trim = function(arg_value){
    if( isNull( arg_value ) == true || arg_value.length <= 0 ) {
        return '' ;
    }
    for( i = 0; i < arg_value.length ; i ++ ) {
        old = arg_value ;
        arg_value = arg_value.replace( "&nbsp;"," " ) ;
        if( old.length == arg_value.length ) {
            break ;
        }
    }
    
    st = 0 ;
    ed = arg_value.length ;
    len = arg_value.length ;
    for( i = 0 ; i < len ; i ++ ) {
        c = arg_value.charAt( i ) ;
        x = arg_value.charCodeAt( i ) ;
        if( c == ' ' || c == '　' || c == '\t' || x == 160 ) {//x==160はsafari対応.
            st = i+1 ;
        }
        else {
            break ;
        }
    }
    c = null ;
    len = arg_value.length-1 ;
    for( i = len ; i >= 0 ; i -- ) {
        c = arg_value.charAt( i ) ;
        x = arg_value.charCodeAt( i ) ;
        if( c == ' ' || c == '　' || c == '\t' || x == 160 ) {//x==160はsafari対応.
            ed = i ;
        }
        else {
            break ;
        }
    }
    c = null ;
    if( st >= ed ) {
        return '' ;
    }
    ret = arg_value.substring( 0,ed ) ;
    if( st > 0 ) {
        ret = ret.substring( st ) ;
    }
    return ret ;
}

// 文字列を置き換える.
changeString = function( base,src,dest ) {
    ret = '' ;
    p = base.indexOf( src ) ;
    if( p == -1 ) {
        return base ;
    }
    if( p == 0 ) {
        ret += dest ;
        ret += base.substring( src.length,base.length ) ;
    }
    else {
        ret += base.substring( 0,p ) ;
        ret += dest ;
        if( src.length != base.length ) {
            ret += base.substring( p+src.length,base.length ) ;
        }
    }
    return ret ;
}

// startWith.
startsWith = function(str,chk) {
    if( isNull( str ) || isNull( chk ) ) {
        return false ;
    }
    str = ""+str ;
    chk = ""+chk ;
    if( str.indexOf( chk ) == 0 ) {
        return true ;
    }
    return false ;
} ;

// endsWith.
endsWith = function(str,chk) {
    if( isNull( str ) || isNull( chk ) ) {
        return false ;
    }
    str = ""+str ;
    chk = ""+chk ;
    var last = str.lastIndexOf( chk ) ;
    if( last != -1 && last == str.length-chk.length ) {
        return true ;
    }
    return false ;
} ;

// 日付を変換.
formatDateByString = function( date,mode ) {
    if( isNull( date ) ) {
        return null ;
    }
    if( isNull( mode ) ) {
        mode = false ;
    }
    var ret = "" + date.getFullYear() ;
    var x = ""+(date.getMonth()+1) ;
    ret += "/" + "00".substring( x.length,2 ) + x ;
    x = ""+date.getDate() ;
    ret += "/" + "00".substring( x.length,2 ) + x ;
    if( mode == true ) {
        ret += "&nbsp;" ;
    }
    else {
        ret += " " ;
    }
    x = ""+date.getHours() ;
    ret += "00".substring( x.length,2 ) + x ;
    x = ""+date.getMinutes() ;
    ret += ":" + "00".substring( x.length,2 ) + x ;
    x = ""+date.getSeconds() ;
    ret += ":" + "00".substring( x.length,2 ) + x ;
    return ret ;
} ;

// パラメータエンコード.
encodeParam = function(params) {
    if( useString( params ) == true ) {
        var x = params.split( "&" ) ;
        var len = x.length ;
        var ret = "" ;
        for( var i = 0 ; i < len ; i ++ ) {
            if( i != 0 ) {
                ret += "&" ;
            }
            var p = x[i].indexOf( "=" ) ;
            if( p != -1 ) {
                var k = x[i].substring( 0,p ) ;
                var v = x[i].substring( p+1,x[i].length ) ;
                x[i] = k + "=" + encodeURIComponent( v ) ;
            }
            ret += x[i] ;
        }
    }
    return ret ;
} ;

// Loop待機.
loopWait = function( timeWait ) {
    var timeStart = new Date().getTime();
    var timeNow = new Date().getTime();
    while( timeNow < (timeStart + timeWait ) ) {
        timeNow = new Date().getTime();
    }
    return;
} ;

// 時間毎に処理実行.
TimerThread = function() { }
TimerThread.prototype = {
    timerId :null,
    callback: function() {
        // 別途実装が必要.
    },
    startup: function( time ) {
        if( this.timerId != null ) {
            this.shutdown() ;
        }
        if( time != null && typeof( time ) == "number" && time > 0 ) {
            this.timerId = setInterval( this.execution.bind( this ),time ) ;
        }
    },
    shutdown: function() {
        if( this.timerId != null ) {
            clearInterval( this.timerId ) ;
            this.timerId = null ;
        }
    },
    execution: function() {
        this.callback() ;
    }
} ;

// 指定IDにBase64形式の画像を表示.
viewImageByBase64 = function( id,mimeType,data ) {
    if( isNull( id ) || useString( mimeType ) == false || isNull( data ) ) {
        return ;
    }
    $(id).src = "data:"+mimeType+";base64,"+data ;
} ;

////////////////////////////////////////////////////////////////////////////////
// ブラウザ系.
////////////////////////////////////////////////////////////////////////////////

// URL遷移.
forwardURL = function( url ) {
    window.location = url ;
}

////////////////////////////////////////////////////////////////////////////////
// ドキュメント系.
////////////////////////////////////////////////////////////////////////////////

// 対象スタイルシートにWidht,Heightを割り当てる.
putStyleWidhtHeight = function( clazz,width,height,etc ) {
    var prop = "" ;
    if( width > 0 ) {
        width /= 100 ;
        var x = Math.ceil( bwWidth() * width ) ;
        prop += " width: "+x+"px;" ;
    }
    if( height > 0 ) {
        height /= 100 ;
        var y = Math.ceil( bwHeight() * height ) ;
        prop += " height: "+y+"px;" ;
    }
    if( etc != null && ( etc = trim( etc ) ).length > 0 ) {
        prop += etc ;
        if( endsWith( etc,";" ) == false ) {
            prop += ";" ;
        }
    }
    if( prop.length > 0 ) {
        add_css_value( clazz,prop ) ;
    }
} ;

// ブラウザWidthを取得.
bwWidth = function() {
    if( isMSIE == true ) {
        return document.body.clientWidth ;
    }
    else {
        return window.innerWidth ;
    }
} ;

// ブラウザHeightを取得.
bwHeight = function() {
    if( isMSIE == true ) {
        return document.body.clientHeight ;
    }
    else {
        return window.innerHeight ;
    }
} ;

// クラス情報を取得.
getElementsByClassName = function(clazz) {
    var ret = [];
    var elems = (document.getElementsByTagName("*") || document.all);
    for (var i = 0; i < elems.length; i++) {
        if (elems[i].className == clazz) {
            ret[ret.length] = elems[i];
        }
    }
    return ret;
}

////////////////////////////////////////////////////////////////////////////////
// スタイルシート書き換え.
////////////////////////////////////////////////////////////////////////////////

// スタイルシートを書き換える.
add_css_value = function( selector, property ) {
    if( isNull( selector ) || isNull( property ) ) {
        return false ;
    }
    var sheets = get_sheets() ;
    if( isNull( sheets ) ) {
        return false ;
    }
    if( isMSIE == true ) {
        if( sheets[0].addRule ) { //IE
            sheets[0].addRule( selector, "{" + property + "}" );
            return true ;
        }
    }
    else {
        if( sheets[0].insertRule ) { //Mozilla
            sheets[0].insertRule( selector + "{" + property + "}", sheets[0].cssRules.length );
            return true ;
        }
    }
    return false;
}

// スタイルシートを取得する.
get_css_value = function( selector, property ) {
    var property = ( property.match( /-/ ) ) ? property.camelize( ) : property;
    var stylesheets = null ;
    var sheets = get_sheets() ;
    if( isNull( sheets ) ) {
        return false ;
    }
    if( isMSIE == true ) {
        stylesheets = sheets[0].rules       //IE
    }
    else {
        stylesheets = sheets[0].cssRules;    //Mozilla
    }
    for( var i = 0, len = stylesheets.length; i < len; i++ ) {
        var css = stylesheets[i];
        if( css.selectorText.toLowerCase( ) == selector.toLowerCase( ) ) {
            return css.style[ property ];
        }
    }
}

// 指定要素での有効スタイルシート値を取得.
get_active_css_value = function( element, property, pseudo ) {
    var sheets = get_sheets() ;
    if( isNull( sheets ) ) {
        return false ;
    }
    if( isMSIE == true ) {
        if( element.currentStyle ) {          //IE
            var property = ( property.match( /-/ ) ) ? property.camelize( ) : property;
            return element.currentStyle[ property.camelize( ) ];
        }
    }
    else {
        if( document.defaultView.getComputedStyle ) {    //Mozilla
            var property = ( isNull( property.match( /-/ ) ) ) ? property.deCamelize( ) : property;
            return document.defaultView.getComputedStyle( element, pseudo ).getPropertyValue( property );
        }
    }
    return "";
}

// スタイルシート一覧を取得.
get_sheets = function() {
    if ( document.styleSheets &&
        document.styleSheets[0] &&
        document.styleSheets[0].title != null ) {
        return document.styleSheets;
    }
    return null ;
}

////////////////////////////////////////////////////////////////////////////////
// Cookie系.
////////////////////////////////////////////////////////////////////////////////

// cookie取得.
getCookie = function(key,  tmp1, tmp2, xx1, xx2, xx3) {
    if( isCookie == false ) {
        return  ;
    }
    tmp1 = " " + document.cookie + ";";
    xx1 = xx2 = 0;
    len = tmp1.length;
    while (xx1 < len) {
        xx2 = tmp1.indexOf(";", xx1);
        tmp2 = tmp1.substring(xx1 + 1, xx2);
        xx3 = tmp2.indexOf("=");
        if (tmp2.substring(0, xx3) == key) {
            return(unescape(tmp2.substring(xx3 + 1, xx2 - xx1 - 1)));
        }
        xx1 = xx2 + 1;
    }
    return("");
}

// cookie登録.
setCookie = function(key, val, tmp) {
    if( isCookie == false ) {
        return  ;
    }
    tmp = key + "=" + escape(val) + "; ";
    tmp += "expires=Tue, 31-Dec-2030 23:59:59; ";
    document.cookie = tmp;
}

// cookie削除.
clearCookie = function(key) {
    if( isCookie == false ) {
        return  ;
    }
    document.cookie = key + "=" + "x; expires=Tue, 1-Jan-1980 00:00:00;";
}

///////////////////////////////////////////////////////////////////////////////
// json.
///////////////////////////////////////////////////////////////////////////////

// jsonp.
jsonp = function( url,params ) {
    if( useString( url ) == false ) {
        return -1 ;
    }
    url += "?t="+new Date().getTime() + "&type=jsonp" ;
    if( useString( params ) ) {
        params = encodeParam( params ) ;
        url += "&" + params ;
    }
    var script = document.createElement('script') ;
    script.charset = 'utf-8' ;
    script.src = url ;
    document.body.appendChild(script) ;
    return 0 ;
}

// json形式に変換.
encodeJSON = function( value ) {
    if( isNull( value ) ) {
        return "[\"\"]" ;
    }
    var t = valueof( value ) ;
    if( t == "array" ) {
        t = null ;
        ret = "" ;
        ret = evalByArrayString( ret,value ) ;
        return ret ;
    }
    else if( t == "map" ) {
        t = null ;
        var ret = "[" ;
        evalByMapString( ret,value ) ;
        return ret + "]" ;
    }
    return "["+evalByString( t,value )+"]" ;
}

// json形式の文字列を変換.
decodeJSON = function( value ) {
    if( isNull( value ) || typeof( value ) != "string" ) {
        return null ;
    }
    var ret = eval( value ) ;
    if( isNull( ret ) != null ) {
        if( valueof( ret ) == "array" && ret.length == 1 ) {
            ret = ret[ 0 ] ;
        }
        return ret ;
    }
    return null ;
}

// Arrayタイプから、eval実行可能内容を生成.
evalByArrayString = function( o,v ) {
    o += "[" ;
    var len = v.length ;
    for( var i = 0 ; i < len ; i ++ ) {
        if( i != 0 ) {
            o += "," ;
        }
        var t = valueof( v[i] ) ;
        if( t == "array" ) {
            o = evalByArrayString( o,v[i] ) ;
        }
        else if( t == "map" ) {
            o = evalByMapString( o,v[i] ) ;
        }
        else {
            o += evalByString( t,v[i] ) ;
        }
    }
    return o + "]" ;
}

// Mapタイプから、eval実行可能内容を生成.
evalByMapString = function( o,v ) {
    o += "{" ;
    var cnt = 0 ;
    for( var k in v ) {
        if( cnt != 0 ) {
            o += "," ;
        }
        o += "\"" + k + "\":" ;
        var t = valueof( v[k] ) ;
        if( t == "array" ) {
            o = evalByArrayString( o,v[k] ) ;
        }
        else if( t == "map" ) {
            o = evalByMapString( o,v[k] ) ;
        }
        else {
            o += evalByString( t,v[k] ) ;
        }
        cnt ++ ;
    }
    return o + "}" ;
}

// 指定タイプから、eval実行可能内容を生成.
evalByString = function( t,v ) {
    if( t == "string" ) {
        return "\""+v+"\"" ;
    }
    else if( t == "boolean" ) {
        return ""+v ;
    }
    else if( t == "number" ) {
        return ""+v ;
    }
    else if( t == "date" ) {
        return "new Date(" + v.getTime() + ")" ;
    }
    else if( t == "function" ) {
        return v.toString() ;
    }
    return "\"\"" ;
}

// 指定内容タイプを取得.
valueof = function( val ) {
    if( isNull( val ) ) {
        return "null" ;
    }
    var t = typeof( val ) ;
    if( t == "string" ) {
        return "string" ;
    }
    if( t == "boolean" ) {
        return "boolean" ;
    }
    else if( t == "number" ) {
        return "number" ;
    }
    else if( t == "function" ) {
        return "function" ;
    }
    else if( t == "object" ) {
        if( val instanceof Date ) {
            return "date" ;
        }
        else if( val instanceof Array ) {
            if( val.length == 0 ) {
                return "array" ;
            }
            return arrayOrMapValueTo( val ) ;
        }
    }
    return arrayOrMapValueTo( val ) ;
}

// 指定内容タイプがArrayかMapか取得.
arrayOrMapValueTo = function( val ) {
    var cnt = 0 ;
    var aryFlg = true ;
    for( var k in val ) {
        if( cnt != k ) {
            cnt ++ ;
            aryFlg = false ;
            break ;
        }
        cnt ++ ;
    }
    if( cnt == 0 ) {
        return "object" ;
    }
    if( aryFlg == true ) {
        return "array" ;
    }
    return "map" ;
}

///////////////////////////////////////////////////////////////////////////////
// md5.
///////////////////////////////////////////////////////////////////////////////
var MD5_T = new Array(0x00000000, 0xd76aa478, 0xe8c7b756, 0x242070db,
              0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613,
              0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1,
              0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e,
              0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51,
              0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681,
              0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87,
              0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9,
              0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122,
              0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60,
              0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085,
              0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8,
              0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7,
              0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d,
              0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314,
              0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb,
              0xeb86d391);

var MD5_round1 = new Array(new Array( 0, 7, 1), new Array( 1,12, 2),
               new Array( 2,17, 3), new Array( 3,22, 4),
               new Array( 4, 7, 5), new Array( 5,12, 6),
               new Array( 6,17, 7), new Array( 7,22, 8),
               new Array( 8, 7, 9), new Array( 9,12,10),
               new Array(10,17,11), new Array(11,22,12),
               new Array(12, 7,13), new Array(13,12,14),
               new Array(14,17,15), new Array(15,22,16));

var MD5_round2 = new Array(new Array( 1, 5,17), new Array( 6, 9,18),
               new Array(11,14,19), new Array( 0,20,20),
               new Array( 5, 5,21), new Array(10, 9,22),
               new Array(15,14,23), new Array( 4,20,24),
               new Array( 9, 5,25), new Array(14, 9,26),
               new Array( 3,14,27), new Array( 8,20,28),
               new Array(13, 5,29), new Array( 2, 9,30),
               new Array( 7,14,31), new Array(12,20,32));

var MD5_round3 = new Array(new Array( 5, 4,33), new Array( 8,11,34),
               new Array(11,16,35), new Array(14,23,36),
               new Array( 1, 4,37), new Array( 4,11,38),
               new Array( 7,16,39), new Array(10,23,40),
               new Array(13, 4,41), new Array( 0,11,42),
               new Array( 3,16,43), new Array( 6,23,44),
               new Array( 9, 4,45), new Array(12,11,46),
               new Array(15,16,47), new Array( 2,23,48));

var MD5_round4 = new Array(new Array( 0, 6,49), new Array( 7,10,50),
               new Array(14,15,51), new Array( 5,21,52),
               new Array(12, 6,53), new Array( 3,10,54),
               new Array(10,15,55), new Array( 1,21,56),
               new Array( 8, 6,57), new Array(15,10,58),
               new Array( 6,15,59), new Array(13,21,60),
               new Array( 4, 6,61), new Array(11,10,62),
               new Array( 2,15,63), new Array( 9,21,64));

function MD5_F(x, y, z) { return (x & y) | (~x & z); }
function MD5_G(x, y, z) { return (x & z) | (y & ~z); }
function MD5_H(x, y, z) { return x ^ y ^ z;          }
function MD5_I(x, y, z) { return y ^ (x | ~z);       }

var MD5_round = new Array(new Array(MD5_F, MD5_round1),
              new Array(MD5_G, MD5_round2),
              new Array(MD5_H, MD5_round3),
              new Array(MD5_I, MD5_round4));

function MD5_pack(n32) {
  return String.fromCharCode(n32 & 0xff) +
     String.fromCharCode((n32 >>> 8) & 0xff) +
     String.fromCharCode((n32 >>> 16) & 0xff) +
     String.fromCharCode((n32 >>> 24) & 0xff);
}

function MD5_unpack(s4) {
  return  s4.charCodeAt(0)        |
     (s4.charCodeAt(1) <<  8) |
     (s4.charCodeAt(2) << 16) |
     (s4.charCodeAt(3) << 24);
}

function MD5_number(n) {
  while (n < 0)
    n += 4294967296;
  while (n > 4294967295)
    n -= 4294967296;
  return n;
}

function MD5_apply_round(x, s, f, abcd, r) {
  var a, b, c, d;
  var kk, ss, ii;
  var t, u;

  a = abcd[0];
  b = abcd[1];
  c = abcd[2];
  d = abcd[3];
  kk = r[0];
  ss = r[1];
  ii = r[2];

  u = f(s[b], s[c], s[d]);
  t = s[a] + u + x[kk] + MD5_T[ii];
  t = MD5_number(t);
  t = ((t<<ss) | (t>>>(32-ss)));
  t += s[b];
  s[a] = MD5_number(t);
}

function MD5_hash(data) {
  var abcd, x, state, s;
  var len, index, padLen, f, r;
  var i, j, k;
  var tmp;

  state = new Array(0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476);
  len = data.length;
  index = len & 0x3f;
  padLen = (index < 56) ? (56 - index) : (120 - index);
  if(padLen > 0) {
    data += "\x80";
    for(i = 0; i < padLen - 1; i++)
      data += "\x00";
  }
  data += MD5_pack(len * 8);
  data += MD5_pack(0);
  len  += padLen + 8;
  abcd = new Array(0, 1, 2, 3);
  x    = new Array(16);
  s    = new Array(4);

  for(k = 0; k < len; k += 64) {
    for(i = 0, j = k; i < 16; i++, j += 4) {
      x[i] = data.charCodeAt(j) |
        (data.charCodeAt(j + 1) <<  8) |
        (data.charCodeAt(j + 2) << 16) |
        (data.charCodeAt(j + 3) << 24);
    }
    for(i = 0; i < 4; i++)
      s[i] = state[i];
    for(i = 0; i < 4; i++) {
      f = MD5_round[i][0];
      r = MD5_round[i][1];
      for(j = 0; j < 16; j++) {
    MD5_apply_round(x, s, f, abcd, r[j]);
    tmp = abcd[0];
    abcd[0] = abcd[3];
    abcd[3] = abcd[2];
    abcd[2] = abcd[1];
    abcd[1] = tmp;
      }
    }

    for(i = 0; i < 4; i++) {
      state[i] += s[i];
      state[i] = MD5_number(state[i]);
    }
  }

  return MD5_pack(state[0]) +
     MD5_pack(state[1]) +
     MD5_pack(state[2]) +
     MD5_pack(state[3]);
}

function MD5_hexhash(data) {
    var i, out, c;
    var bit128;

    bit128 = MD5_hash(data);
    out = "";
    for(i = 0; i < 16; i++) {
    c = bit128.charCodeAt(i);
    out += "0123456789abcdef".charAt((c>>4) & 0xf);
    out += "0123456789abcdef".charAt(c & 0xf);
    }
    return out;
}

///////////////////////////////////////////////////////////////////////////////
// base64
///////////////////////////////////////////////////////////////////////////////
/*
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
var base64DecodeChars = new Array(
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,
    -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 62, -1, -1, -1, 63,
    52, 53, 54, 55, 56, 57, 58, 59, 60, 61, -1, -1, -1, -1, -1, -1,
    -1,  0,  1,  2,  3,  4,  5,  6,  7,  8,  9, 10, 11, 12, 13, 14,
    15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, -1, -1, -1, -1, -1,
    -1, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40,
    41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, -1, -1, -1, -1, -1);

encodeBase64 = function(str) {
    var out, i, len;
    var c1, c2, c3;
    len = str.length;
    i = 0;
    out = "";
    while(i < len) {
    c1 = str.charCodeAt(i++) & 0xff;
    if(i == len)
    {
        out += base64EncodeChars.charAt(c1 >> 2);
        out += base64EncodeChars.charAt((c1 & 0x3) << 4);
        out += "==";
        break;
    }
    c2 = str.charCodeAt(i++);
    if(i == len)
    {
        out += base64EncodeChars.charAt(c1 >> 2);
        out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
        out += base64EncodeChars.charAt((c2 & 0xF) << 2);
        out += "=";
        break;
    }
    c3 = str.charCodeAt(i++);
    out += base64EncodeChars.charAt(c1 >> 2);
    out += base64EncodeChars.charAt(((c1 & 0x3)<< 4) | ((c2 & 0xF0) >> 4));
    out += base64EncodeChars.charAt(((c2 & 0xF) << 2) | ((c3 & 0xC0) >>6));
    out += base64EncodeChars.charAt(c3 & 0x3F);
    }
    return out;
}

decodeBase64 = function(str) {
    var c1, c2, c3, c4;
    var i, len, out;
    len = str.length;
    i = 0;
    out = "";
    while(i < len) {
    do {
        c1 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    } while(i < len && c1 == -1);
    if(c1 == -1)
        break;
    do {
        c2 = base64DecodeChars[str.charCodeAt(i++) & 0xff];
    } while(i < len && c2 == -1);
    if(c2 == -1)
        break;
    out += String.fromCharCode((c1 << 2) | ((c2 & 0x30) >> 4));
    do {
        c3 = str.charCodeAt(i++) & 0xff;
        if(c3 == 61)
        return out;
        c3 = base64DecodeChars[c3];
    } while(i < len && c3 == -1);
    if(c3 == -1)
        break;
    out += String.fromCharCode(((c2 & 0XF) << 4) | ((c3 & 0x3C) >> 2));
    do {
        c4 = str.charCodeAt(i++) & 0xff;
        if(c4 == 61)
        return out;
        c4 = base64DecodeChars[c4];
    } while(i < len && c4 == -1);
    if(c4 == -1)
        break;
    out += String.fromCharCode(((c3 & 0x03) << 6) | c4);
    }
    return out;
}
*/
