text: if (typeof Object.assign != 'function') {
    Object.assign = function(target, varArgs) {
      'use strict';
      if (target == null) {
        throw new TypeError('Cannot convert undefined or null to object');
      }
  
      var to = Object(target);
  
      for (var index = 1; index < arguments.length; index++) {
        var nextSource = arguments[index];
  
        if (nextSource != null) { // Skip over if undefined or null
          for (var nextKey in nextSource) {
            // Avoid bugs when hasOwnProperty is shadowed
            if (Object.prototype.hasOwnProperty.call(nextSource, nextKey)) {
              to[nextKey] = nextSource[nextKey];
            }
          }
        }
      }
      return to;
    };
  }
  
  export function camelCase(name) {
    return name.replace(/([\:\-\_]+(.))/g, function (_, separator, letter, offset) {
      return offset ? letter.toUpperCase() : letter;
    });
  }
  
  export function isUndefined(value) {
    return typeof value === 'undefined'
  }
  
  export function isDefined(value) {
    return typeof value !== 'undefined'
  }
  
  export function isObject(value) {
    return value !== null && typeof value === 'object'
  }
  
  export function isString(value) {
    return typeof value === 'string'
  }
  
  export function isNumber(value) {
    return typeof value === 'number'
  }
  
  export function isFunction(value) {
    return typeof value === 'function'
  }
  
  export function isIosInAppBrowser() {
    return navigator.userAgent.match(/iphone|ipod|ipad/i) && !navigator.userAgent.match(/safari/i)
  }
  
  export function isFacebookOwnedInAppBrowser() {
    return !!navigator.userAgent.match(/instagram/i) || 
      !!navigator.userAgent.match(/fban/i) || 
      !!navigator.userAgent.match(/fbav/i)
  }
  
  export function isPlayrggApp() {
    return !!navigator.userAgent.match(/PLAYR\.gg/)
  }
  
  export function isLockedDownInAppBrowser() {
    return isIosInAppBrowser() && (!!navigator.userAgent.match(/instagram/i) || !!navigator.userAgent.match(/fban/i))
  }
  
  export function isInIframe() {
    return !!window.frameElement
  }
  
  export function objectExtend(a, b) {
  
    // Don't touch 'null' or 'undefined' objects.
    if (a == null || b == null) {
      return a;
    }
  
    Object.keys(b).forEach(function (key) {
      if (Object.prototype.toString.call(b[key]) == '[object Object]') {
        if (Object.prototype.toString.call(a[key]) != '[object Object]') {
          a[key] = b[key];
        } else {
          a[key] = objectExtend(a[key], b[key]);
        }
      } else {
        a[key] = b[key];
      }
    });
  
    return a;
  };
  
  /**
   * Assemble url from two segments
   * 
   * @author Sahat Yalkabov <https://github.com/sahat>
   * @copyright Method taken from https://github.com/sahat/satellizer
   * 
   * @param  {String} baseUrl Base url
   * @param  {String} url     URI
   * @return {String}
   */
  export function joinUrl(baseUrl, url) {
    if (/^(?:[a-z]+:)?\/\//i.test(url)) {
      return url;
    }
    let joined = [baseUrl, url].join('/');
    let normalize = function (str) {
      return str
        .replace(/[\/]+/g, '/')
        .replace(/\/\?/g, '?')
        .replace(/\/\#/g, '#')
        .replace(/\:\//g, '://');
    };
    return normalize(joined);
  }
  
  /**
   * Get full path based on current location
   * 
   * @author Sahat Yalkabov <https://github.com/sahat>
   * @copyright Method taken from https://github.com/sahat/satellizer
   * 
   * @param  {Location} location
   * @return {String}
   */
  export function getFullUrlPath(location) {
    const isHttps = location.protocol === 'https:';
    return location.protocol + '//' + location.hostname +
      ':' + (location.port || (isHttps ? '443' : '80')) +
      (/^\//.test(location.pathname) ? location.pathname : '/' + location.pathname);
  }
  
  /**
   * Parse query string variables
   * 
   * @author Sahat Yalkabov <https://github.com/sahat>
   * @copyright Method taken from https://github.com/sahat/satellizer
   * 
   * @param  {String} Query string
   * @return {String}
   */
  export function parseQueryString(str) {
    let obj = {};
    let key;
    let value;
    (str || '').split('&').forEach((keyValue) => {
      if (keyValue) {
        value = keyValue.split('=');
        key = decodeURIComponent(value[0]);
        obj[key] = (!!value[1]) ? decodeURIComponent(value[1]) : true;
      }
    });
    return obj;
  }
  
  /**
   * Decode base64 string
   * @author Sahat Yalkabov <https://github.com/sahat>
   * @copyright Method taken from https://github.com/sahat/satellizer
   * 
   * @param  {String} str base64 encoded string
   * @return {Object}
   */
  export function decodeBase64(str) {
    let buffer;
    if (typeof module !== 'undefined' && module.exports) {
      try {
        buffer = require('buffer').Buffer;
      } catch (err) {
        // noop
      }
    }
  
    let fromCharCode = String.fromCharCode;
  
    let re_btou = new RegExp([
      '[\xC0-\xDF][\x80-\xBF]',
      '[\xE0-\xEF][\x80-\xBF]{2}',
      '[\xF0-\xF7][\x80-\xBF]{3}'
    ].join('|'), 'g');
  
    let cb_btou = function (cccc) {
      switch (cccc.length) {
        case 4:
          let cp = ((0x07 & cccc.charCodeAt(0)) << 18)
            | ((0x3f & cccc.charCodeAt(1)) << 12)
            | ((0x3f & cccc.charCodeAt(2)) << 6)
            | (0x3f & cccc.charCodeAt(3));
          let offset = cp - 0x10000;
          return (fromCharCode((offset >>> 10) + 0xD800)
          + fromCharCode((offset & 0x3FF) + 0xDC00));
        case 3:
          return fromCharCode(
            ((0x0f & cccc.charCodeAt(0)) << 12)
            | ((0x3f & cccc.charCodeAt(1)) << 6)
            | (0x3f & cccc.charCodeAt(2))
          );
        default:
          return fromCharCode(
            ((0x1f & cccc.charCodeAt(0)) << 6)
            | (0x3f & cccc.charCodeAt(1))
          );
      }
    };
  
    let btou = function (b) {
      return b.replace(re_btou, cb_btou);
    };
  
    let _decode = buffer ? function (a) {
      return (a.constructor === buffer.constructor
        ? a : new buffer(a, 'base64')).toString();
    }
      : function (a) {
      return btou(atob(a));
    };
  
    return _decode(
      String(str).replace(/[-_]/g, function (m0) {
        return m0 === '-' ? '+' : '/';
      })
        .replace(/[^A-Za-z0-9\+\/]/g, '')
    );
  }
  
  export function parseCookies(str) {
    if (str.length === 0) return {};
    const parsed = {};
    const pattern = new RegExp('\\s*;\\s*');
    str.split(pattern).forEach((i) => {
      const [encodedKey, encodedValue] = i.split('=');
      const key = decodeURIComponent(encodedKey);
      const value = decodeURIComponent(encodedValue);
      parsed[key] = value;
    });
    return parsed;
  };
  
  export function formatOptions(options) {
    const { path, domain, expires, secure, sameSite } = options;
    return [
      typeof path === 'undefined' || path === null
        ? '' : ';path=' + path,
      typeof domain === 'undefined' || domain === null
        ? '' : ';domain=' + domain,
      typeof expires === 'undefined' || expires === null
        ? '' : ';expires=' + expires.toUTCString(),
      typeof secure === 'undefined' || secure === null || secure === false
        ? '' : ';secure',
      typeof sameSite === 'undefined' || sameSite === null
        ? '' : ';samesite=' + sameSite
    ].join('');
  };
  
  export function formatCookie(key, value, options) {
    return [
      encodeURIComponent(key),
      '=',
      encodeURIComponent(value),
      formatOptions(options)
    ].join('');
  };
  