UNPKG

amplitude-js

Version:
1,527 lines (1,484 loc) 167 kB
import { isBrowserEnv, prototypeJsFix } from '@amplitude/utils'; import md5 from 'blueimp-md5'; import queryString from 'query-string'; import UAParser from '@amplitude/ua-parser-js'; import { AnalyticsConnector } from '@amplitude/analytics-connector'; 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); } 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 there are already other operations, then don't add $clearAll. * If $clearAll already in an Identify object, don't allow other operations to be added. * @private */ Identify.prototype.clearAll = function () { if (Object.keys(this.userPropertiesOperations).length > 0) { if (!Object.prototype.hasOwnProperty.call(this.userPropertiesOperations, AMP_OP_CLEAR_ALL)) { utils.log.error('Need to send $clearAll on its own Identify object without any other operations, skipping $clearAll'); } return this; } this.userPropertiesOperations[AMP_OP_CLEAR_ALL] = '-'; return this; }; /** * Prepend a value or values to a user property. * Prepend means inserting the value or values at the front of a list. * If the user property does not have a value set yet, * it will be initialized to an empty list before the new values are prepended. * 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 prepended. * @public * @param {string} property - The user property key. * @param {number|string|list|object} value - A value or values to prepend. * 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().prepend('ab-tests', 'new-user-tests'); * identify.prepend('some_list', [1, 2, 3, 4, 'values']); * amplitude.identify(identify); // send the Identify call */ Identify.prototype.prepend = function (property, value) { this._addOperation(AMP_OP_PREPEND, property, value); return this; }; /** * Sets the value of a given user property. If a value already exists, it will be overwriten with the new value. * @public * @param {string} property - The user property key. * @param {number|string|list|boolean|object} value - A value or values to set. * 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().set('user_type', 'beta'); * identify.set('name', {'first': 'John', 'last': 'Doe'}); // dict is flattened and becomes name.first: John, name.last: Doe * amplitude.identify(identify); // send the Identify call */ Identify.prototype.set = function (property, value) { this._addOperation(AMP_OP_SET, property, value); return this; }; /** * Sets the value of a given user property only once. Subsequent setOnce operations on that user property will be ignored; * however, that user property can still be modified through any of the other operations. * Useful for capturing properties such as 'initial_signup_date', 'initial_referrer', etc. * @public * @param {string} property - The user property key. * @param {number|string|list|boolean|object} value - A value or values to set once. * 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().setOnce('sign_up_date', '2016-04-01'); * amplitude.identify(identify); // send the Identify call */ Identify.prototype.setOnce = function (property, value) { this._addOperation(AMP_OP_SET_ONCE, property, value); return this; }; /** * Unset and remove a user property. This user property will no longer show up in a user's profile. * @public * @param {string} property - The user property key. * @return {Identify} Returns the same Identify object, allowing you to chain multiple method calls together. * @example var identify = new amplitude.Identify().unset('user