text: /*
  https://gist.github.com/diafygi/90a3e80ca1c2793220e5/
  */
  var BASE58_ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz';
  var arrayToBase58 = function (B) {
      var d = [],   //the array for storing the stream of base58 digits
          s = "",   //the result string variable that will be returned
          j,        //the iterator variable for the base58 digit array (d)
          c,        //the carry amount variable that is used to overflow from the current base58 digit to the next base58 digit
          n;        //a temporary placeholder variable for the current base58 digit
      for (var i = 0; i < B.length; i++) { //loop through each byte in the input stream
          j = 0,                           //reset the base58 digit iterator
              c = B[i];                        //set the initial carry amount equal to the current byte amount
          s += c || s.length ^ i ? "" : 1; //prepend the result string with a "1" (0 in base58) if the byte stream is zero and non-zero bytes haven't been seen yet (to ensure correct decode length)
          while (j in d || c) {            //start looping through the digits until there are no more digits and no carry amount
              n = d[j];                    //set the placeholder for the current base58 digit
              n = n ? n * 256 + c : c;     //shift the current base58 one byte and add the carry amount (or just add the carry amount if this is a new digit)
              c = n / 58 | 0;              //find the new carry amount (floored integer of current digit divided by 58)
              d[j] = n % 58;               //reset the current base58 digit to the remainder (the carry amount will pass on the overflow)
              j++                          //iterate to the next base58 digit
          }
      }
      while (j--)        //since the base58 digits are backwards, loop through them in reverse order
          s += BASE58_ALPHABET[d[j]]; //lookup the character associated with each base58 digit
      return s          //return the final base58 string
  }
  function base58ToArray(S) { var d = [], b = [], i, j, c, n; for (i in S) { j = 0, c = BASE58_ALPHABET.indexOf(S[i]); if (c < 0) return undefined; c || b.length ^ i ? i : b.push(0); while (j in d || c) { n = d[j]; n = n ? n * 58 + c : c; c = n >> 8; d[j] = n % 256; j++ } } while (j--) b.push(d[j]); return new Uint8Array(b) };
  
  function arrayToBase64(arr) {
      let binary = '';
      let len = arr.byteLength;
      for (let i = 0; i < len; i++) {
          binary += String.fromCharCode(arr[i]);
      }
      return window.btoa(binary);
  }
  
  function arrayToBase64url(byteArray) {
      return btoa(Array.from(new Uint8Array(byteArray)).map(val => {
          return String.fromCharCode(val);
      }).join('')).replace(/\+/g, '-').replace(/\//g, '_').replace(/\=/g, '');
  }
  
  function base64urlToArray(b64urlstring) {
      return new Uint8Array(atob(b64urlstring.replace(/-/g, '+').replace(/_/g, '/')).split('').map(val => {
          return val.charCodeAt(0);
      }));
  }
  
  /* Randomize array in-place using Durstenfeld shuffle algorithm */
  // steps: [1, array.length - 1]
  function shuffleArray(array, steps, rndSource) {
      let end = array.length - 1 - steps;
      if (end < 0) end = 0;
      for (let i = array.length - 1; i > end; i--) {
          let j = Math.floor(rndSource.random() * (i + 1));
          let temp = array[i];
          array[i] = array[j];
          array[j] = temp;
      }
  }
  
  function getHumanReadableSize(bytes) {
      var thresh = 1024;
      if(Math.abs(bytes) < thresh) {
          return bytes + ' B';
      }
      var units = ['KB','MB','GB','TB','PB','EB','ZB','YB'];
      var u = -1;
      do {
          bytes /= thresh;
          ++u;
      } while(Math.abs(bytes) >= thresh && u < units.length - 1);
      return bytes.toFixed(1)+' '+units[u];
  }
  
  function getRandomInRange(min, max, isFloat) {
      if (isFloat)
          return Math.random() * (max - min) + min;
      else
          return Math.floor(Math.random() * (max - min) + min);
  }
  
  function trace(s) {
      console.log(s);
  }
  
  // https://medium.com/@karenmarkosyan/how-to-manage-promises-into-dynamic-queue-with-vanilla-javascript-9d0d1f8d4df5
  class Queue {
      constructor() {
          this.queue = [];
          this.pendingPromise = false;
      }
  
      enqueue(promise) {
        return new Promise((resolve, reject) => {
            this.queue.push({
                promise,
                resolve,
                reject,
            });
            this.dequeue();
        });
      }
  
      dequeue() {
        if (this.workingOnPromise) {
          return false;
        }
        const item = this.queue.shift();
        if (!item) {
          return false;
        }
        try {
          this.workingOnPromise = true;
          item.promise()
            .then((value) => {
              this.workingOnPromise = false;
              item.resolve(value);
              this.dequeue();
            })
            .catch(err => {
              this.workingOnPromise = false;
              item.reject(err);
              this.dequeue();
            })
        } catch (err) {
          this.workingOnPromise = false;
          item.reject(err);
          this.dequeue();
        }
        return true;
      }
  }
  
  
  module.exports.arrayToBase58 = arrayToBase58
  module.exports.base58ToArray = base58ToArray
  module.exports.arrayToBase64 = arrayToBase64
  module.exports.arrayToBase64url = arrayToBase64url
  module.exports.base64urlToArray = base64urlToArray
  module.exports.shuffleArray = shuffleArray
  module.exports.getHumanReadableSize = getHumanReadableSize
  module.exports.getRandomInRange = getRandomInRange
  module.exports.trace = trace
  module.exports.Queue = Queue
  