var escapeChars =
{
  '¢' : 'cent',
  '£' : 'pound',
  '¥' : 'yen',
  '€': 'euro',
  '©' :'copy',
  '®' : 'reg',
  '<' : 'lt',
  '>' : 'gt',
  '"' : 'quot',
  '&' : 'amp',
  '\'' : '#39'
};

var regexString = '[';
for(var key in escapeChars) {
  regexString += key;
}
regexString += ']';

var regex = new RegExp( regexString, 'g');

function escapeHTML(str)
{
  if ( typeof str != 'string' )
  {
    return str;
  }
  return str.replace ( regex , function ( m )
  {
    return '&' + escapeChars[m] + ';';
  } );
}

function _getKeys ( o , keys )
{
    if ( typeof o == 'object' )
    {
        var localkeys = Object.keys ( o );
        for ( var i = 0 ; i < localkeys.length ; i++ )
        {
            keys.push ( localkeys[i] );
            _getKeys ( o[localkeys[i]] , keys );
        }
    }
}


function serializeObject ( o )
{
    var keys = [];
    _getKeys ( o , keys );
    keys.sort();
    var cad = JSON.stringify ( o , keys );
    var result = "";
    for ( var i = 0 ; i < cad.length ; i++ )
    {
        if ( cad.charCodeAt ( i ) > 127 )
        {
           result += "\\u" + ("000" + cad.charCodeAt ( i ).toString ( 16 ) ).substr ( -4 );
        }
        else
        {
           result += cad[i];
        }
    }
    return result;
}

function readFile ( file )
{
  return new Promise ( ( resolve ) =>
  {
    const reader = new FileReader();
    reader.onloadend = () => resolve ( reader.result );
    reader.readAsDataURL ( file )
  } );
}

function formatDate ( date )
{
  return date.toLocaleString ( undefined , { year: "numeric", month: "2-digit", day: "2-digit" , seconds: false } );
}

function formatDateTime ( date )
{
  return date.toLocaleString ( undefined , { year: "numeric", month: "2-digit", day: "2-digit" , hour: "2-digit" , minute: "2-digit" , seconds: false } );
}

function hexToB64 ( str )
{
  if ( str.indexOf ( '0x' )  == 0 )
  {
    str = str.substr ( 2 );
  }
  return btoa ( str.match(/\w{2}/g ).map ( function ( a ) { return String.fromCharCode ( parseInt ( a , 16 ) );} ).join ( "" ) );
}

function b64ToHex(str) {
  const raw = atob(str);
  let result = '';
  for (let i = 0; i < raw.length; i++) {
    const hex = raw.charCodeAt(i).toString(16);
    result += (hex.length === 2 ? hex : '0' + hex);
  }
  return result;
}

// Encoding UTF8 ⇢ base64

function b64EncodeUnicode(str) {
  return btoa(encodeURIComponent(str).replace(/%([0-9A-F]{2})/g, function(match, p1) {
      return String.fromCharCode(parseInt(p1, 16))
  }))
}

// Decoding base64 ⇢ UTF8

function b64DecodeUnicode(str) {
  return decodeURIComponent(Array.prototype.map.call(atob(str), function(c) {
      return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2)
  }).join(''))
}

function hexStringToUint8Array(hexString){
  if ( hexString.indexOf ( '0x' ) == 0 )
  {
      hexString = hexString.substr ( 2 );
  }
  return new Uint8Array(hexString.match(/.{1,2}/g).map(byte => parseInt(byte, 16)));
}

function uint8ArrayToHexString ( bytes )
{
  return bytes.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '');
}
