(function (exports) {

var Enc = exports.Enc = {};

Enc.bufToBin = function (buf) {
  var bin = '';
  // cannot use .map() because Uint8Array would return only 0s
  buf.forEach(function (ch) {
    bin += String.fromCharCode(ch);
  });
  return bin;
};

Enc.bufToHex = function toHex(u8) {
  var hex = [];
  var i, h;
  var len = (u8.byteLength || u8.length);

  for (i = 0; i < len; i += 1) {
    h = u8[i].toString(16);
    if (h.length % 2) { h = '0' + h; }
    hex.push(h);
  }

  return hex.join('').toLowerCase();
};

Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
  var r = str % 4;
  if (2 === r) {
    str += '==';
  } else if (3 === r) {
    str += '=';
  }
  return str.replace(/-/g, '+').replace(/_/g, '/');
};

Enc.base64ToBuf = function (b64) {
  return Enc.binToBuf(atob(b64));
};
Enc.binToBuf = function (bin) {
  var arr = bin.split('').map(function (ch) {
    return ch.charCodeAt(0);
  });
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};
Enc.bufToHex = function (u8) {
  var hex = [];
  var i, h;
  var len = (u8.byteLength || u8.length);

  for (i = 0; i < len; i += 1) {
    h = u8[i].toString(16);
    if (h.length % 2) { h = '0' + h; }
    hex.push(h);
  }

  return hex.join('').toLowerCase();
};
Enc.numToHex = function (d) {
  d = d.toString(16);
  if (d.length % 2) {
    return '0' + d;
  }
  return d;
};

Enc.bufToUrlBase64 = function (u8) {
  return Enc.bufToBase64(u8)
    .replace(/\+/g, '-').replace(/\//g, '_').replace(/=/g, '');
};

Enc.bufToBase64 = function (u8) {
  var bin = '';
  u8.forEach(function (i) {
    bin += String.fromCharCode(i);
  });
  return btoa(bin);
};

Enc.hexToBuf = function (hex) {
  var arr = [];
  hex.match(/.{2}/g).forEach(function (h) {
    arr.push(parseInt(h, 16));
  });
  return 'undefined' !== typeof Uint8Array ? new Uint8Array(arr) : arr;
};

Enc.numToHex = function (d) {
  d = d.toString(16);
  if (d.length % 2) {
    return '0' + d;
  }
  return d;
};


//
// JWK to SSH (tested working)
//
Enc.base64ToHex = function (b64) {
  var bin = atob(Enc.urlBase64ToBase64(b64));
  return Enc.binToHex(bin);
};

Enc.binToHex = function (bin) {
  return bin.split('').map(function (ch) {
    var h = ch.charCodeAt(0).toString(16);
    if (h.length % 2) { h = '0' + h; }
    return h;
  }).join('');
};

Enc.hexToBase64 = function (hex) {
  return btoa(Enc.hexToBin(hex));
};

Enc.hexToBin = function (hex) {
  return hex.match(/.{2}/g).map(function (h) {
    return String.fromCharCode(parseInt(h, 16));
  }).join('');
};

Enc.urlBase64ToBase64 = function urlsafeBase64ToBase64(str) {
  var r = str % 4;
  if (2 === r) {
    str += '==';
  } else if (3 === r) {
    str += '=';
  }
  return str.replace(/-/g, '+').replace(/_/g, '/');
};


}('undefined' !== typeof exports ? module.exports : window ));