amplitude-js
Version:
Javascript library for Amplitude Analytics
1,479 lines (1,439 loc) • 295 kB
JavaScript
(function (global, factory) {
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
typeof define === 'function' && define.amd ? define('amplitude', factory) :
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.amplitude = factory());
})(this, (function () { 'use strict';
function ownKeys(object, enumerableOnly) {
var keys = Object.keys(object);
if (Object.getOwnPropertySymbols) {
var symbols = Object.getOwnPropertySymbols(object);
enumerableOnly && (symbols = symbols.filter(function (sym) {
return Object.getOwnPropertyDescriptor(object, sym).enumerable;
})), keys.push.apply(keys, symbols);
}
return keys;
}
function _objectSpread2(target) {
for (var i = 1; i < arguments.length; i++) {
var source = null != arguments[i] ? arguments[i] : {};
i % 2 ? ownKeys(Object(source), !0).forEach(function (key) {
_defineProperty(target, key, source[key]);
}) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) {
Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
});
}
return target;
}
function _typeof(obj) {
"@babel/helpers - typeof";
return _typeof = "function" == typeof Symbol && "symbol" == typeof Symbol.iterator ? function (obj) {
return typeof obj;
} : function (obj) {
return obj && "function" == typeof Symbol && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj;
}, _typeof(obj);
}
function _classCallCheck(instance, Constructor) {
if (!(instance instanceof Constructor)) {
throw new TypeError("Cannot call a class as a function");
}
}
function _defineProperties(target, props) {
for (var i = 0; i < props.length; i++) {
var descriptor = props[i];
descriptor.enumerable = descriptor.enumerable || false;
descriptor.configurable = true;
if ("value" in descriptor) descriptor.writable = true;
Object.defineProperty(target, _toPropertyKey(descriptor.key), descriptor);
}
}
function _createClass(Constructor, protoProps, staticProps) {
if (protoProps) _defineProperties(Constructor.prototype, protoProps);
if (staticProps) _defineProperties(Constructor, staticProps);
Object.defineProperty(Constructor, "prototype", {
writable: false
});
return Constructor;
}
function _defineProperty(obj, key, value) {
key = _toPropertyKey(key);
if (key in obj) {
Object.defineProperty(obj, key, {
value: value,
enumerable: true,
configurable: true,
writable: true
});
} else {
obj[key] = value;
}
return obj;
}
function _toConsumableArray(arr) {
return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _unsupportedIterableToArray(arr) || _nonIterableSpread();
}
function _arrayWithoutHoles(arr) {
if (Array.isArray(arr)) return _arrayLikeToArray(arr);
}
function _iterableToArray(iter) {
if (typeof Symbol !== "undefined" && iter[Symbol.iterator] != null || iter["@@iterator"] != null) return Array.from(iter);
}
function _unsupportedIterableToArray(o, minLen) {
if (!o) return;
if (typeof o === "string") return _arrayLikeToArray(o, minLen);
var n = Object.prototype.toString.call(o).slice(8, -1);
if (n === "Object" && o.constructor) n = o.constructor.name;
if (n === "Map" || n === "Set") return Array.from(o);
if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen);
}
function _arrayLikeToArray(arr, len) {
if (len == null || len > arr.length) len = arr.length;
for (var i = 0, arr2 = new Array(len); i < len; i++) arr2[i] = arr[i];
return arr2;
}
function _nonIterableSpread() {
throw new TypeError("Invalid attempt to spread non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
function _createForOfIteratorHelper(o, allowArrayLike) {
var it = typeof Symbol !== "undefined" && o[Symbol.iterator] || o["@@iterator"];
if (!it) {
if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") {
if (it) o = it;
var i = 0;
var F = function () {};
return {
s: F,
n: function () {
if (i >= o.length) return {
done: true
};
return {
done: false,
value: o[i++]
};
},
e: function (e) {
throw e;
},
f: F
};
}
throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method.");
}
var normalCompletion = true,
didErr = false,
err;
return {
s: function () {
it = it.call(o);
},
n: function () {
var step = it.next();
normalCompletion = step.done;
return step;
},
e: function (e) {
didErr = true;
err = e;
},
f: function () {
try {
if (!normalCompletion && it.return != null) it.return();
} finally {
if (didErr) throw err;
}
}
};
}
function _toPrimitive(input, hint) {
if (typeof input !== "object" || input === null) return input;
var prim = input[Symbol.toPrimitive];
if (prim !== undefined) {
var res = prim.call(input, hint || "default");
if (typeof res !== "object") return res;
throw new TypeError("@@toPrimitive must return a primitive value.");
}
return (hint === "string" ? String : Number)(input);
}
function _toPropertyKey(arg) {
var key = _toPrimitive(arg, "string");
return typeof key === "symbol" ? key : String(key);
}
/**
* Checks whether we're in a browser environment
*
* @returns Answer to given question
*/
function isBrowserEnv() {
return typeof window === 'object' && (window === null || window === void 0 ? void 0 : window.document) !== undefined;
}
/**
* Fixes browser edge case where Prototype.js injects Array.prototype.toJSON and breaks the built-in JSON.stringify()
*
* @returns true if Array.prototype.toJSON was deleted, false if not
*/
var prototypeJsFix = function () {
var _a;
if (isBrowserEnv()) {
var augmentedWindow = window;
var augmentedArray = Array;
if (augmentedWindow.Prototype !== undefined && ((_a = augmentedArray.prototype) === null || _a === void 0 ? void 0 : _a.toJSON) !== undefined) {
delete augmentedArray.prototype.toJSON;
return true;
}
}
return false;
};
var Constants = {
DEFAULT_INSTANCE: '$default_instance',
API_VERSION: 2,
MAX_STRING_LENGTH: 4096,
MAX_PROPERTY_KEYS: 1000,
IDENTIFY_EVENT: '$identify',
GROUP_IDENTIFY_EVENT: '$groupidentify',
EVENT_LOG_URL: 'api.amplitude.com',
EVENT_LOG_EU_URL: 'api.eu.amplitude.com',
DYNAMIC_CONFIG_URL: 'regionconfig.amplitude.com',
DYNAMIC_CONFIG_EU_URL: 'regionconfig.eu.amplitude.com',
// localStorageKeys
LAST_EVENT_ID: 'amplitude_lastEventId',
LAST_EVENT_TIME: 'amplitude_lastEventTime',
LAST_IDENTIFY_ID: 'amplitude_lastIdentifyId',
LAST_SEQUENCE_NUMBER: 'amplitude_lastSequenceNumber',
SESSION_ID: 'amplitude_sessionId',
// Used in cookie as well
DEVICE_ID: 'amplitude_deviceId',
OPT_OUT: 'amplitude_optOut',
USER_ID: 'amplitude_userId',
// indexes of properties in cookie v2 storage format
DEVICE_ID_INDEX: 0,
USER_ID_INDEX: 1,
OPT_OUT_INDEX: 2,
SESSION_ID_INDEX: 3,
LAST_EVENT_TIME_INDEX: 4,
EVENT_ID_INDEX: 5,
IDENTIFY_ID_INDEX: 6,
SEQUENCE_NUMBER_INDEX: 7,
COOKIE_TEST_PREFIX: 'amp_cookie_test',
COOKIE_PREFIX: 'amp',
// Storage options
STORAGE_DEFAULT: '',
STORAGE_COOKIES: 'cookies',
STORAGE_NONE: 'none',
STORAGE_LOCAL: 'localStorage',
STORAGE_SESSION: 'sessionStorage',
// revenue keys
REVENUE_EVENT: 'revenue_amount',
REVENUE_PRODUCT_ID: '$productId',
REVENUE_QUANTITY: '$quantity',
REVENUE_PRICE: '$price',
REVENUE_REVENUE_TYPE: '$revenueType',
AMP_DEVICE_ID_PARAM: 'amp_device_id',
// url param
AMP_REFERRER_PARAM: 'amp_referrer',
// url param for overwriting the document.refer
REFERRER: 'referrer',
REFERRING_DOMAIN: 'referring_domain',
// UTM Params
UTM_SOURCE: 'utm_source',
UTM_MEDIUM: 'utm_medium',
UTM_CAMPAIGN: 'utm_campaign',
UTM_TERM: 'utm_term',
UTM_CONTENT: 'utm_content',
ATTRIBUTION_EVENT: '[Amplitude] Attribution Captured',
TRANSPORT_HTTP: 'http',
TRANSPORT_BEACON: 'beacon'
};
/*
* UTF-8 encoder/decoder
* http://www.webtoolkit.info/
*/
var UTF8 = {
encode: function encode(s) {
var utftext = '';
for (var n = 0; n < s.length; n++) {
var c = s.charCodeAt(n);
if (c < 128) {
utftext += String.fromCharCode(c);
} else if (c > 127 && c < 2048) {
utftext += String.fromCharCode(c >> 6 | 192);
utftext += String.fromCharCode(c & 63 | 128);
} else {
utftext += String.fromCharCode(c >> 12 | 224);
utftext += String.fromCharCode(c >> 6 & 63 | 128);
utftext += String.fromCharCode(c & 63 | 128);
}
}
return utftext;
},
decode: function decode(utftext) {
var s = '';
var i = 0;
var c = 0,
c1 = 0,
c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
s += String.fromCharCode(c);
i++;
} else if (c > 191 && c < 224) {
c1 = utftext.charCodeAt(i + 1);
s += String.fromCharCode((c & 31) << 6 | c1 & 63);
i += 2;
} else {
c1 = utftext.charCodeAt(i + 1);
c2 = utftext.charCodeAt(i + 2);
s += String.fromCharCode((c & 15) << 12 | (c1 & 63) << 6 | c2 & 63);
i += 3;
}
}
return s;
}
};
/* global globalThis */
var GlobalScope = function () {
if (typeof globalThis !== 'undefined') {
return globalThis;
}
if (typeof window !== 'undefined') {
return window;
}
if (typeof self !== 'undefined') {
return self;
}
if (typeof global !== 'undefined') {
return global;
}
}();
/*
* Base64 encoder/decoder
* http://www.webtoolkit.info/
*/
var Base64 = {
_keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=',
encode: function encode(input) {
try {
if (GlobalScope.btoa && GlobalScope.atob) {
return GlobalScope.btoa(unescape(encodeURIComponent(input)));
}
} catch (e) {
//log(e);
}
return Base64._encode(input);
},
_encode: function _encode(input) {
var output = '';
var chr1, chr2, chr3, enc1, enc2, enc3, enc4;
var i = 0;
input = UTF8.encode(input);
while (i < input.length) {
chr1 = input.charCodeAt(i++);
chr2 = input.charCodeAt(i++);
chr3 = input.charCodeAt(i++);
enc1 = chr1 >> 2;
enc2 = (chr1 & 3) << 4 | chr2 >> 4;
enc3 = (chr2 & 15) << 2 | chr3 >> 6;
enc4 = chr3 & 63;
if (isNaN(chr2)) {
enc3 = enc4 = 64;
} else if (isNaN(chr3)) {
enc4 = 64;
}
output = output + Base64._keyStr.charAt(enc1) + Base64._keyStr.charAt(enc2) + Base64._keyStr.charAt(enc3) + Base64._keyStr.charAt(enc4);
}
return output;
},
decode: function decode(input) {
try {
if (GlobalScope.btoa && GlobalScope.atob) {
return decodeURIComponent(escape(GlobalScope.atob(input)));
}
} catch (e) {
//log(e);
}
return Base64._decode(input);
},
_decode: function _decode(input) {
var output = '';
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9+/=]/g, '');
while (i < input.length) {
enc1 = Base64._keyStr.indexOf(input.charAt(i++));
enc2 = Base64._keyStr.indexOf(input.charAt(i++));
enc3 = Base64._keyStr.indexOf(input.charAt(i++));
enc4 = Base64._keyStr.indexOf(input.charAt(i++));
chr1 = enc1 << 2 | enc2 >> 4;
chr2 = (enc2 & 15) << 4 | enc3 >> 2;
chr3 = (enc3 & 3) << 6 | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 !== 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 !== 64) {
output = output + String.fromCharCode(chr3);
}
}
output = UTF8.decode(output);
return output;
}
};
/**
* toString ref.
* @private
*/
var toString = Object.prototype.toString;
/**
* Return the type of `val`.
* @private
* @param {Mixed} val
* @return {String}
* @api public
*/
function type (val) {
switch (toString.call(val)) {
case '[object Date]':
return 'date';
case '[object RegExp]':
return 'regexp';
case '[object Arguments]':
return 'arguments';
case '[object Array]':
return 'array';
case '[object Error]':
return 'error';
}
if (val === null) {
return 'null';
}
if (val === undefined) {
return 'undefined';
}
if (val !== val) {
return 'nan';
}
if (val && val.nodeType === 1) {
return 'element';
}
if (typeof Buffer !== 'undefined' && typeof Buffer.isBuffer === 'function' && Buffer.isBuffer(val)) {
return 'buffer';
}
val = val.valueOf ? val.valueOf() : Object.prototype.valueOf.apply(val);
return _typeof(val);
}
var logLevels = {
DISABLE: 0,
ERROR: 1,
WARN: 2,
INFO: 3
};
var logLevel = logLevels.WARN;
var setLogLevel = function setLogLevel(logLevelName) {
if (Object.prototype.hasOwnProperty.call(logLevels, logLevelName)) {
logLevel = logLevels[logLevelName];
}
};
var getLogLevel = function getLogLevel() {
return logLevel;
};
var log = {
error: function error(s) {
if (logLevel >= logLevels.ERROR) {
_log(s);
}
},
warn: function warn(s) {
if (logLevel >= logLevels.WARN) {
_log(s);
}
},
info: function info(s) {
if (logLevel >= logLevels.INFO) {
_log(s);
}
}
};
var _log = function _log(s) {
try {
console.log('[Amplitude] ' + s);
} catch (e) {
// console logging not available
}
};
var isEmptyString = function isEmptyString(str) {
return !str || str.length === 0;
};
var sessionStorageEnabled = function sessionStorageEnabled() {
try {
if (GlobalScope.sessionStorage) {
return true;
}
} catch (e) {
// sessionStorage disabled
}
return false;
};
// truncate string values in event and user properties so that request size does not get too large
var truncate = function truncate(value) {
if (type(value) === 'array') {
for (var i = 0; i < value.length; i++) {
value[i] = truncate(value[i]);
}
} else if (type(value) === 'object') {
for (var key in value) {
if (key in value) {
value[key] = truncate(value[key]);
}
}
} else {
value = _truncateValue(value);
}
return value;
};
var _truncateValue = function _truncateValue(value) {
if (type(value) === 'string') {
return value.length > Constants.MAX_STRING_LENGTH ? value.substring(0, Constants.MAX_STRING_LENGTH) : value;
}
return value;
};
var validateInput = function validateInput(input, name, expectedType) {
if (type(input) !== expectedType) {
log.error('Invalid ' + name + ' input type. Expected ' + expectedType + ' but received ' + type(input));
return false;
}
return true;
};
var validateDeviceId = function validateDeviceId(deviceId) {
if (!validateInput(deviceId, 'deviceId', 'string')) {
return false;
}
if (deviceId.indexOf('.') >= 0) {
log.error("Device IDs may not contain '.' characters. Value will be ignored: \"".concat(deviceId, "\""));
return false;
}
return true;
};
var validateTransport = function validateTransport(transport) {
if (!validateInput(transport, 'transport', 'string')) {
return false;
}
if (transport !== Constants.TRANSPORT_HTTP && transport !== Constants.TRANSPORT_BEACON) {
log.error("transport value must be one of '".concat(Constants.TRANSPORT_BEACON, "' or '").concat(Constants.TRANSPORT_HTTP, "'"));
return false;
}
if (transport !== Constants.TRANSPORT_HTTP && typeof navigator !== 'undefined' && !navigator.sendBeacon) {
log.error("browser does not support sendBeacon, so transport must be HTTP");
return false;
}
return true;
};
// do some basic sanitization and type checking, also catch property dicts with more than 1000 key/value pairs
var validateProperties = function validateProperties(properties) {
var propsType = type(properties);
if (propsType !== 'object') {
log.error('Error: invalid properties format. Expecting Javascript object, received ' + propsType + ', ignoring');
return {};
}
if (Object.keys(properties).length > Constants.MAX_PROPERTY_KEYS) {
log.error('Error: too many properties (more than 1000), ignoring');
return {};
}
var copy = {}; // create a copy with all of the valid properties
for (var property in properties) {
if (!Object.prototype.hasOwnProperty.call(properties, property)) {
continue;
}
// validate key
var key = property;
var keyType = type(key);
if (keyType !== 'string') {
key = String(key);
log.warn('WARNING: Non-string property key, received type ' + keyType + ', coercing to string "' + key + '"');
}
// validate value
var value = validatePropertyValue(key, properties[property]);
if (value === null) {
continue;
}
copy[key] = value;
}
return copy;
};
var invalidValueTypes = ['nan', 'function', 'arguments', 'regexp', 'element'];
var validatePropertyValue = function validatePropertyValue(key, value) {
var valueType = type(value);
if (invalidValueTypes.indexOf(valueType) !== -1) {
log.warn('WARNING: Property key "' + key + '" with invalid value type ' + valueType + ', ignoring');
value = null;
} else if (valueType === 'undefined') {
value = null;
} else if (valueType === 'error') {
value = String(value);
log.warn('WARNING: Property key "' + key + '" with value type error, coercing to ' + value);
} else if (valueType === 'array') {
// check for nested arrays or objects
var arrayCopy = [];
for (var i = 0; i < value.length; i++) {
var element = value[i];
var elemType = type(element);
if (elemType === 'array') {
log.warn('WARNING: Cannot have ' + elemType + ' nested in an array property value, skipping');
continue;
} else if (elemType === 'object') {
arrayCopy.push(validateProperties(element));
} else {
arrayCopy.push(validatePropertyValue(key, element));
}
}
value = arrayCopy;
} else if (valueType === 'object') {
value = validateProperties(value);
}
return value;
};
var validateGroups = function validateGroups(groups) {
var groupsType = type(groups);
if (groupsType !== 'object') {
log.error('Error: invalid groups format. Expecting Javascript object, received ' + groupsType + ', ignoring');
return {};
}
var copy = {}; // create a copy with all of the valid properties
for (var group in groups) {
if (!Object.prototype.hasOwnProperty.call(groups, group)) {
continue;
}
// validate key
var key = group;
var keyType = type(key);
if (keyType !== 'string') {
key = String(key);
log.warn('WARNING: Non-string groupType, received type ' + keyType + ', coercing to string "' + key + '"');
}
// validate value
var value = validateGroupName(key, groups[group]);
if (value === null) {
continue;
}
copy[key] = value;
}
return copy;
};
var validateGroupName = function validateGroupName(key, groupName) {
var groupNameType = type(groupName);
if (groupNameType === 'string') {
return groupName;
}
if (groupNameType === 'date' || groupNameType === 'number' || groupNameType === 'boolean') {
groupName = String(groupName);
log.warn('WARNING: Non-string groupName, received type ' + groupNameType + ', coercing to string "' + groupName + '"');
return groupName;
}
if (groupNameType === 'array') {
// check for nested arrays or objects
var arrayCopy = [];
for (var i = 0; i < groupName.length; i++) {
var element = groupName[i];
var elemType = type(element);
if (elemType === 'array' || elemType === 'object') {
log.warn('WARNING: Skipping nested ' + elemType + ' in array groupName');
continue;
} else if (elemType === 'string') {
arrayCopy.push(element);
} else if (elemType === 'date' || elemType === 'number' || elemType === 'boolean') {
element = String(element);
log.warn('WARNING: Non-string groupName, received type ' + elemType + ', coercing to string "' + element + '"');
arrayCopy.push(element);
}
}
return arrayCopy;
}
log.warn('WARNING: Non-string groupName, received type ' + groupNameType + '. Please use strings or array of strings for groupName');
};
// parses the value of a url param (for example ?gclid=1234&...)
var getQueryParam = function getQueryParam(name, query) {
name = name.replace(/[[]/, '\\[').replace(/[\]]/, '\\]');
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)');
var results = regex.exec(query);
return results === null ? undefined : decodeURIComponent(results[1].replace(/\+/g, ' '));
};
var isWebWorkerEnvironment = function isWebWorkerEnvironment() {
return typeof WorkerGlobalScope !== 'undefined';
};
var validateSessionId = function validateSessionId(sessionId) {
if (validateInput(sessionId, 'sessionId', 'number') && new Date(sessionId).getTime() > 0) {
return true;
}
log.error("sessionId value must in milliseconds since epoch (Unix Timestamp)");
return false;
};
var getLocation = function getLocation() {
return GlobalScope.location;
};
var getHost = function getHost(url) {
var defaultHostname = GlobalScope.location ? GlobalScope.location.hostname : '';
if (url) {
if (typeof document !== 'undefined') {
var a = document.createElement('a');
a.href = url;
return a.hostname || defaultHostname;
}
if (typeof URL === 'function') {
var u = new URL(url);
return u.hostname || defaultHostname;
}
}
return defaultHostname;
};
var utils = {
setLogLevel: setLogLevel,
getLogLevel: getLogLevel,
logLevels: logLevels,
log: log,
isEmptyString: isEmptyString,
isWebWorkerEnvironment: isWebWorkerEnvironment,
getQueryParam: getQueryParam,
sessionStorageEnabled: sessionStorageEnabled,
truncate: truncate,
validateGroups: validateGroups,
validateInput: validateInput,
validateProperties: validateProperties,
validateDeviceId: validateDeviceId,
validateTransport: validateTransport,
validateSessionId: validateSessionId,
getLocation: getLocation,
getHost: getHost
};
var get$1 = function get(name) {
try {
var ca = document.cookie.split(';');
var value = null;
for (var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) === ' ') {
c = c.substring(1, c.length);
}
if (c.indexOf(name) === 0) {
value = c.substring(name.length, c.length);
break;
}
}
return value;
} catch (e) {
return null;
}
};
var getAll = function getAll(name) {
try {
var cookieArray = document.cookie.split(';').map(function (c) {
return c.trimStart();
});
var values = [];
var _iterator = _createForOfIteratorHelper(cookieArray),
_step;
try {
for (_iterator.s(); !(_step = _iterator.n()).done;) {
var cookie = _step.value;
while (cookie.charAt(0) === ' ') {
cookie = cookie.substring(1);
}
if (cookie.indexOf(name) === 0) {
values.push(cookie.substring(name.length));
}
}
} catch (err) {
_iterator.e(err);
} finally {
_iterator.f();
}
return values;
} catch (e) {
return [];
}
};
var set$1 = function set(name, value, opts) {
var expires = value !== null ? opts.expirationDays : -1;
if (expires) {
var date = new Date();
date.setTime(date.getTime() + expires * 24 * 60 * 60 * 1000);
expires = date;
}
var str = name + '=' + value;
if (expires) {
str += '; expires=' + expires.toUTCString();
}
str += '; path=/';
if (opts.domain) {
str += '; domain=' + opts.domain;
}
if (opts.secure) {
str += '; Secure';
}
if (opts.sameSite) {
str += '; SameSite=' + opts.sameSite;
}
document.cookie = str;
};
var getLastEventTime = function getLastEventTime() {
var cookie = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : '';
var strValue = cookie.split('.')[Constants.LAST_EVENT_TIME_INDEX];
var parsedValue;
if (strValue) {
parsedValue = parseInt(strValue, 32);
}
if (parsedValue) {
return parsedValue;
} else {
utils.log.warn("unable to parse malformed cookie: ".concat(cookie));
return 0;
}
};
var sortByEventTime = function sortByEventTime(cookies) {
return _toConsumableArray(cookies).sort(function (c1, c2) {
var t1 = getLastEventTime(c1);
var t2 = getLastEventTime(c2);
// sort c1 first if its last event time is more recent
// i.e its event time integer is larger that c2's
return t2 - t1;
});
};
// test that cookies are enabled - navigator.cookiesEnabled yields false positives in IE, need to test directly
var areCookiesEnabled = function areCookiesEnabled() {
var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var cookieName = Constants.COOKIE_TEST_PREFIX;
if (typeof document === 'undefined') {
return false;
}
var _areCookiesEnabled = false;
try {
var uid = String(Date.now());
set$1(cookieName, uid, opts);
utils.log.info("Testing if cookies available");
_areCookiesEnabled = get$1(cookieName + '=') === uid;
} catch (e) {
utils.log.warn("Error thrown when checking for cookies. Reason: \"".concat(e, "\""));
} finally {
utils.log.info("Cleaning up cookies availability test");
set$1(cookieName, null, opts);
}
return _areCookiesEnabled;
};
var baseCookie = {
set: set$1,
get: get$1,
getAll: getAll,
getLastEventTime: getLastEventTime,
sortByEventTime: sortByEventTime,
areCookiesEnabled: areCookiesEnabled
};
// A URL safe variation on the the list of Base64 characters
var base64Chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';
var base64Id = function base64Id() {
var str = '';
for (var i = 0; i < 22; ++i) {
str += base64Chars.charAt(Math.floor(Math.random() * 64));
}
return str;
};
// Utility that finds top level domain to write to
var topDomain = function topDomain(url) {
var host = utils.getHost(url);
var parts = host.split('.');
var levels = [];
var cname = '_tldtest_' + base64Id();
if (utils.isWebWorkerEnvironment()) return '';
for (var i = parts.length - 2; i >= 0; --i) {
levels.push(parts.slice(i).join('.'));
}
for (var _i = 0; _i < levels.length; ++_i) {
var domain = levels[_i];
var opts = {
domain: '.' + domain
};
baseCookie.set(cname, 1, opts);
if (baseCookie.get(cname)) {
baseCookie.set(cname, null, opts);
return domain;
}
}
return '';
};
/*
* Cookie data
*/
var _options = {
expirationDays: undefined,
domain: undefined
};
var reset = function reset() {
_options = {
expirationDays: undefined,
domain: undefined
};
};
var options = function options(opts) {
if (arguments.length === 0) {
return _options;
}
opts = opts || {};
_options.expirationDays = opts.expirationDays;
_options.secure = opts.secure;
_options.sameSite = opts.sameSite;
var domain = !utils.isEmptyString(opts.domain) ? opts.domain : '.' + topDomain(utils.getLocation().href);
var token = Math.random();
_options.domain = domain;
set('amplitude_test', token);
var stored = get('amplitude_test');
if (!stored || stored !== token) {
domain = null;
}
remove('amplitude_test');
_options.domain = domain;
return _options;
};
var _domainSpecific = function _domainSpecific(name) {
// differentiate between cookies on different domains
var suffix = '';
if (_options.domain) {
suffix = _options.domain.charAt(0) === '.' ? _options.domain.substring(1) : _options.domain;
}
return name + suffix;
};
var get = function get(name) {
var nameEq = _domainSpecific(name) + '=';
var value = baseCookie.get(nameEq);
try {
if (value) {
return JSON.parse(Base64.decode(value));
}
} catch (e) {
return null;
}
return null;
};
var set = function set(name, value) {
try {
baseCookie.set(_domainSpecific(name), Base64.encode(JSON.stringify(value)), _options);
return true;
} catch (e) {
return false;
}
};
var setRaw = function setRaw(name, value) {
try {
baseCookie.set(_domainSpecific(name), value, _options);
return true;
} catch (e) {
return false;
}
};
var getRaw = function getRaw(name) {
var nameEq = _domainSpecific(name) + '=';
return baseCookie.get(nameEq);
};
var remove = function remove(name) {
try {
baseCookie.set(_domainSpecific(name), null, _options);
return true;
} catch (e) {
return false;
}
};
var Cookie = {
reset: reset,
options: options,
get: get,
set: set,
remove: remove,
setRaw: setRaw,
getRaw: getRaw
};
var WorkerStorage = /*#__PURE__*/function () {
function WorkerStorage() {
_classCallCheck(this, WorkerStorage);
this.map = new Map();
this.length = 0;
}
_createClass(WorkerStorage, [{
key: "key",
value: function key(index) {
var keys = Array.from(this.map.keys());
var key = keys[index];
return this.map.get(key);
}
}, {
key: "getItem",
value: function getItem(key) {
return this.map.get(key);
}
}, {
key: "setItem",
value: function setItem(key, value) {
if (!this.map.has(key)) {
this.length += 1;
}
this.map.set(key, value);
}
}, {
key: "removeItem",
value: function removeItem(key) {
if (this.map.has(key)) {
this.length -= 1;
this.map["delete"](key);
}
}
}, {
key: "clear",
value: function clear() {
this.map.clear();
this.length = 0;
}
}]);
return WorkerStorage;
}();
/*
* Implement localStorage to support Firefox 2-3 and IE 5-7
*/
var localStorage;
{
// test that Window.localStorage is available and works
var windowLocalStorageAvailable = function windowLocalStorageAvailable() {
var uid = new Date();
var result;
try {
GlobalScope.localStorage.setItem(uid, uid);
result = GlobalScope.localStorage.getItem(uid) === String(uid);
GlobalScope.localStorage.removeItem(uid);
return result;
} catch (e) {
// localStorage not available
}
return false;
};
if (windowLocalStorageAvailable()) {
localStorage = GlobalScope.localStorage;
} else if (typeof GlobalScope !== 'undefined' && GlobalScope.globalStorage) {
// Firefox 2-3 use globalStorage
// See https://developer.mozilla.org/en/dom/storage#globalStorage
try {
localStorage = GlobalScope.globalStorage[GlobalScope.location.hostname];
} catch (e) {
// Something bad happened...
}
} else if (typeof document !== 'undefined') {
// IE 5-7 use userData
// See http://msdn.microsoft.com/en-us/library/ms531424(v=vs.85).aspx
var div = document.createElement('div'),
attrKey = 'localStorage';
div.style.display = 'none';
document.getElementsByTagName('head')[0].appendChild(div);
if (div.addBehavior) {
div.addBehavior('#default#userdata');
localStorage = {
length: 0,
setItem: function setItem(k, v) {
div.load(attrKey);
if (!div.getAttribute(k)) {
this.length++;
}
div.setAttribute(k, v);
div.save(attrKey);
},
getItem: function getItem(k) {
div.load(attrKey);
return div.getAttribute(k);
},
removeItem: function removeItem(k) {
div.load(attrKey);
if (div.getAttribute(k)) {
this.length--;
}
div.removeAttribute(k);
div.save(attrKey);
},
clear: function clear() {
div.load(attrKey);
var i = 0;
var attr;
while (attr = div.XMLDocument.documentElement.attributes[i++]) {
div.removeAttribute(attr.name);
}
div.save(attrKey);
this.length = 0;
},
key: function key(k) {
div.load(attrKey);
return div.XMLDocument.documentElement.attributes[k];
}
};
div.load(attrKey);
localStorage.length = div.XMLDocument.documentElement.attributes.length;
}
} else if (utils.isWebWorkerEnvironment()) {
// Web worker
localStorage = new WorkerStorage();
}
if (!localStorage) {
/* eslint-disable no-unused-vars */
localStorage = {
length: 0,
setItem: function setItem(k, v) {},
getItem: function getItem(k) {},
removeItem: function removeItem(k) {},
clear: function clear() {},
key: function key(k) {}
};
/* eslint-enable no-unused-vars */
}
}
var localStorage$1 = localStorage;
/*
* Abstraction layer for cookie storage.
* Uses cookie if available, otherwise fallback to localstorage.
*/
var cookieStorage = function cookieStorage() {
this.storage = null;
};
cookieStorage.prototype.getStorage = function (disableCookies) {
if (this.storage !== null) {
return this.storage;
}
if (!disableCookies && baseCookie.areCookiesEnabled()) {
this.storage = Cookie;
} else {
// if cookies disabled, fallback to localstorage
// note: localstorage does not persist across subdomains
var keyPrefix = 'amp_cookiestore_';
this.storage = {
_options: {
expirationDays: undefined,
domain: undefined,
secure: false
},
reset: function reset() {
this._options = {
expirationDays: undefined,
domain: undefined,
secure: false
};
},
options: function options(opts) {
if (arguments.length === 0) {
return this._options;
}
opts = opts || {};
this._options.expirationDays = opts.expirationDays || this._options.expirationDays;
// localStorage is specific to subdomains
this._options.domain = opts.domain || this._options.domain || GlobalScope && GlobalScope.location && GlobalScope.location.hostname;
return this._options.secure = opts.secure || false;
},
get: function get(name) {
try {
return JSON.parse(localStorage$1.getItem(keyPrefix + name));
} catch (e) {} /* eslint-disable-line no-empty */
return null;
},
set: function set(name, value) {
try {
localStorage$1.setItem(keyPrefix + name, JSON.stringify(value));
return true;
} catch (e) {} /* eslint-disable-line no-empty */
return false;
},
remove: function remove(name) {
try {
localStorage$1.removeItem(keyPrefix + name);
} catch (e) {
return false;
}
}
};
}
return this.storage;
};
var _storageOptionExists;
var storageOptionExists = (_storageOptionExists = {}, _defineProperty(_storageOptionExists, Constants.STORAGE_COOKIES, true), _defineProperty(_storageOptionExists, Constants.STORAGE_NONE, true), _defineProperty(_storageOptionExists, Constants.STORAGE_LOCAL, true), _defineProperty(_storageOptionExists, Constants.STORAGE_SESSION, true), _storageOptionExists);
/**
* MetadataStorage involves SDK data persistance
* storage priority: cookies -> localStorage -> in memory
* This priority can be overriden by setting the storage options.
* if in localStorage, unable track users between subdomains
* if in memory, then memory can't be shared between different tabs
*/
var MetadataStorage = /*#__PURE__*/function () {
function MetadataStorage(_ref) {
var storageKey = _ref.storageKey,
disableCookies = _ref.disableCookies,
domain = _ref.domain,
secure = _ref.secure,
sameSite = _ref.sameSite,
expirationDays = _ref.expirationDays,
storage = _ref.storage;
_classCallCheck(this, MetadataStorage);
this.storageKey = storageKey;
this.domain = domain;
this.secure = secure;
this.sameSite = sameSite;
this.expirationDays = expirationDays;
this.cookieDomain = '';
var loc = utils.getLocation() ? utils.getLocation().href : undefined;
var writableTopDomain = !disableCookies ? topDomain(loc) : '';
this.cookieDomain = domain || (writableTopDomain ? '.' + writableTopDomain : null);
if (storageOptionExists[storage]) {
this.storage = storage;
} else {
var disableCookieStorage = disableCookies || !baseCookie.areCookiesEnabled({
domain: this.cookieDomain,
secure: this.secure,
sameSite: this.sameSite,
expirationDays: this.expirationDays
});
if (disableCookieStorage) {
this.storage = Constants.STORAGE_LOCAL;
} else {
this.storage = Constants.STORAGE_COOKIES;
}
}
}
_createClass(MetadataStorage, [{
key: "getCookieStorageKey",
value: function getCookieStorageKey() {
if (!this.domain) {
return this.storageKey;
}
var suffix = this.domain.charAt(0) === '.' ? this.domain.substring(1) : this.domain;
return "".concat(this.storageKey).concat(suffix ? "_".concat(suffix) : '');
}
/*
* Data is saved as delimited values rather than JSO to minimize cookie space
* Should not change order of the items
*/
}, {
key: "save",
value: function save(_ref2) {
var deviceId = _ref2.deviceId,
userId = _ref2.userId,
optOut = _ref2.optOut,
sessionId = _ref2.sessionId,
lastEventTime = _ref2.lastEventTime,
eventId = _ref2.eventId,
identifyId = _ref2.identifyId,
sequenceNumber = _ref2.sequenceNumber;
if (this.storage === Constants.STORAGE_NONE) {
return;
}
var value = [deviceId, Base64.encode(userId || ''),
// used to convert not unicode to alphanumeric since cookies only use alphanumeric
optOut ? '1' : '', sessionId ? sessionId.toString(32) : '0',
// generated when instantiated, timestamp (but re-uses session id in cookie if not expired) @TODO clients may want custom session id
lastEventTime ? lastEventTime.toString(32) : '0',
// last time an event was set
eventId ? eventId.toString(32) : '0', identifyId ? identifyId.toString(32) : '0', sequenceNumber ? sequenceNumber.toString(32) : '0'].join('.');
switch (this.storage) {
case Constants.STORAGE_SESSION:
if (GlobalScope.sessionStorage) {
GlobalScope.sessionStorage.setItem(this.storageKey, value);
}
break;
case Constants.STORAGE_LOCAL:
localStorage$1.setItem(this.storageKey, value);
break;
case Constants.STORAGE_COOKIES:
this.saveCookie(value);
break;
}
}
}, {
key: "saveCookie",
value: function saveCookie(value) {
baseCookie.set(this.getCookieStorageKey(), value, {
domain: this.cookieDomain,
secure: this.secure,
sameSite: this.sameSite,
expirationDays: this.expirationDays
});
}
}, {
key: "load",
value: function load() {
var _this = this;
var str;
if (this.storage === Constants.STORAGE_COOKIES) {
var cookieKey = this.getCookieStorageKey() + '=';
var allCookies = baseCookie.getAll(cookieKey);
if (allCookies.length === 0 || allCookies.length === 1) {
str = allCookies[0];
} else {
// dedup cookies by deleting them all and restoring
// the one with the most recent event time
var latestCookie = baseCookie.sortByEventTime(allCookies)[0];
allCookies.forEach(function () {
return baseCookie.set(_this.getCookieStorageKey(), null, {});
});
this.saveCookie(latestCookie);
str = baseCookie.get(cookieKey);
}
}
if (!str) {
str = localStorage$1.getItem(this.storageKey);
}
if (!str) {
try {
str = GlobalScope.sessionStorage && GlobalScope.sessionStorage.getItem(this.storageKey);
} catch (e) {
utils.log.info("window.sessionStorage unavailable. Reason: \"".concat(e, "\""));
}
}
if (!str) {
return null;
}
var values = str.split('.');
var userId = null;
if (values[Constants.USER_ID_INDEX]) {
try {
userId = Base64.decode(values[Constants.USER_ID_INDEX]);
} catch (e) {
userId = null;
}
}
return {
deviceId: values[Constants.DEVICE_ID_INDEX],
userId: userId,
optOut: values[Constants.OPT_OUT_INDEX] === '1',
sessionId: parseInt(values[Constants.SESSION_ID_INDEX], 32),
lastEventTime: parseInt(values[Constants.LAST_EVENT_TIME_INDEX], 32),
eventId: parseInt(values[Constants.EVENT_ID_INDEX], 32),
identifyId: parseInt(values[Constants.IDENTIFY_ID_INDEX], 32),
sequenceNumber: parseInt(values[Constants.SEQUENCE_NUMBER_INDEX], 32)
};
}
/**
* Clears any saved metadata storage
* @constructor AmplitudeClient
* @public
* @return {boolean} True if metadata was cleared, false if none existed
*/
}, {
key: "clear",
value: function clear() {
var str;
if (this.storage === Constants.STORAGE_COOKIES) {
str = baseCookie.get(this.getCookieStorageKey() + '=');
baseCookie.set(this.getCookieStorageKey(), null, {
domain: this.cookieDomain,
secure: this.secure,
sameSite: this.sameSite,
expirationDays: 0
});
}
if (!str) {
str = localStorage$1.getItem(this.storageKey);
localStorage$1.clear();
}
if (!str) {
try {
str = GlobalScope.sessionStorage && GlobalScope.sessionStorage.getItem(this.storageKey);
GlobalScope.sessionStorage.clear();
} catch (e) {
utils.log.info("window.sessionStorage unavailable. Reason: \"".concat(e, "\""));
}
}
return !!str;
}
}]);
return MetadataStorage;
}();
var getUtmData = function getUtmData(rawCookie, query) {
// Translate the utmz cookie format into url query string format.
var cookie = rawCookie ? '?' + rawCookie.split('.').slice(-1)[0].replace(/\|/g, '&') : '';
var fetchParam = function fetchParam(queryName, query, cookieName, cookie) {
return utils.getQueryParam(queryName, query) || utils.getQueryParam(cookieName, cookie);
};
var utmSource = fetchParam(Constants.UTM_SOURCE, query, 'utmcsr', cookie);
var utmMedium = fetchParam(Constants.UTM_MEDIUM, query, 'utmcmd', cookie);
var utmCampaign = fetchParam(Constants.UTM_CAMPAIGN, query, 'utmccn', cookie);
var utmTerm = fetchParam(Constants.UTM_TERM, query, 'utmctr', cookie);
var utmContent = fetchParam(Constants.UTM_CONTENT, query, 'utmcct', cookie);
var utmData = {};
var addIfNotNull = function addIfNotNull(key, value) {
if (!utils.isEmptyString(value)) {
utmData[key] = value;
}
};
addIfNotNull(Constants.UTM_SOURCE, utmSource);
addIfNotNull(Constants.UTM_MEDIUM, utmMedium);
addIfNotNull(Constants.UTM_CAMPAIGN, utmCampaign);
addIfNotNull(Constants.UTM_TERM, utmTerm);
addIfNotNull(Constants.UTM_CONTENT, utmContent);
return utmData;
};
/*
* Wrapper for a user properties JSON object that supports operations.
* Note: if a user property is used in multiple operations on the same Identify object,
* only the first operation will be saved, and the rest will be ignored.
*/
var AMP_OP_ADD = '$add';
var AMP_OP_APPEND = '$append';
var AMP_OP_CLEAR_ALL = '$clearAll';
var AMP_OP_PREPEND = '$prepend';
var AMP_OP_SET = '$set';
var AMP_OP_SET_ONCE = '$setOnce';
var AMP_OP_UNSET = '$unset';
var AMP_OP_PREINSERT = '$preInsert';
var AMP_OP_POSTINSERT = '$postInsert';
var AMP_OP_REMOVE = '$remove';
/**
* Identify API - instance constructor. Identify objects are a wrapper for user property operations.
* Each method adds a user property operation to the Identify object, and returns the same Identify object,
* allowing you to chain multiple method calls together.
* Note: if the same user property is used in multiple operations on a single Identify object,
* only the first operation on that property will be saved, and the rest will be ignored.
* @constructor Identify
* @public
* @example var identify = new amplitude.Identify();
*/
var Identify = function Identify() {
this.userPropertiesOperations = {};
this.properties = []; // keep track of keys that have been added
};
/**
* Increment a user property by a given value (can also be negative to decrement).
* If the user property does not have a value set yet, it will be initialized to 0 before being incremented.
* @public
* @param {string} property - The user property key.
* @param {number|string} value - The amount by which to increment the user property. Allows numbers as strings (ex: '123').
* @return {Identify} Returns the same Identify object, allowing you to chain multiple method calls together.
* @example var identify = new amplitude.Identify().add('karma', 1).add('friends', 1);
* amplitude.identify(identify); // send the Identify call
*/
Identify.prototype.add = function (property, value) {
if (type(value) === 'number' || type(value) === 'string') {
this._addOperation(AMP_OP_ADD, property, value);
} else {
utils.log.error('Unsupported type for value: ' + type(value) + ', expecting number or string');
}
return this;
};
/**
* Append a value or values to a user property.
* If the user property does not have a value set yet,
* it will be initialized to an empty list before the new values are appended.
* If the user property has an existing value and it is not a list,
* the existing value will be converted into a list with the new values appended.
* @public
* @param {string} property - The user property key.
* @param {number|string|list|object} value - A value or values to append.
* Values can be numbers, strings, lists, or object (key:value dict will be flattened).
* @return {Identify} Returns the same Identify object, allowing you to chain multiple method calls together.
* @example var identify = new amplitude.Identify().append('ab-tests', 'new-user-tests');
* identify.append('some_list', [1, 2, 3, 4, 'values']);
* amplitude.identify(identify); // send the Identify call
*/
Identify.prototype.append = function (property, value) {
this._addOperation(AMP_OP_APPEND, property, value);
return this;
};
/**
* Clear all user properties for the current user.
* SDK user should instead call amplitude.clearUserProperties() instead of using this.
* $clearAll needs to be sent on its own Identify object. If t