UNPKG

amplitude-js

Version:
1,642 lines (1,382 loc) 191 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : typeof define === 'function' && define.amd ? define('amplitude', factory) : (global = global || self, global.amplitude = factory()); }(this, function () { 'use strict'; function _typeof(obj) { if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof = function (obj) { return typeof obj; }; } else { _typeof = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _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, descriptor.key, descriptor); } } function _createClass(Constructor, protoProps, staticProps) { if (protoProps) _defineProperties(Constructor.prototype, protoProps); if (staticProps) _defineProperties(Constructor, staticProps); return Constructor; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } var Constants = { DEFAULT_INSTANCE: '$default_instance', API_VERSION: 2, MAX_STRING_LENGTH: 4096, MAX_PROPERTY_KEYS: 1000, IDENTIFY_EVENT: '$identify', GROUP_IDENTIFY_EVENT: '$groupidentify', // 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', COOKIE_TEST_PREFIX: 'amp_cookie_test', COOKIE_PREFIX: "amp", // 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 REFERRER: 'referrer', // 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' }; /* jshint bitwise: false */ /* * 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; } }; /* jshint bitwise: false */ /* * Base64 encoder/decoder * http://www.webtoolkit.info/ */ var Base64 = { _keyStr: 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=', encode: function encode(input) { try { if (window.btoa && window.atob) { return window.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 (window.btoa && window.atob) { return decodeURIComponent(escape(window.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 (logLevels.hasOwnProperty(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 (window.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 (value.hasOwnProperty(key)) { 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; }; // 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 (!properties.hasOwnProperty(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 (!groups.hasOwnProperty(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 utils = { setLogLevel: setLogLevel, getLogLevel: getLogLevel, logLevels: logLevels, log: log, isEmptyString: isEmptyString, getQueryParam: getQueryParam, sessionStorageEnabled: sessionStorageEnabled, truncate: truncate, validateGroups: validateGroups, validateInput: validateInput, validateProperties: validateProperties }; var getLocation = function getLocation() { return window.location; }; // 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; }; var get = 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 set = 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; }; // 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 uid = String(new Date()); try { var cookieName = Constants.COOKIE_TEST_PREFIX + base64Id(); set(cookieName, uid, opts); var _areCookiesEnabled = get(cookieName + '=') === uid; set(cookieName, null, opts); return _areCookiesEnabled; } catch (e) {} return false; }; var baseCookie = { set: set, get: get, areCookiesEnabled: areCookiesEnabled }; var getHost = function getHost(url) { var a = document.createElement('a'); a.href = url; return a.hostname || location.hostname; }; var topDomain = function topDomain(url) { var host = getHost(url); var parts = host.split('.'); var levels = []; var cname = '_tldtest_' + base64Id(); 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(getLocation().href); var token = Math.random(); _options.domain = domain; set$1('amplitude_test', token); var stored = get$1('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$1 = 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$1 = 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$1, set: set$1, remove: remove, setRaw: setRaw, getRaw: getRaw }; /* jshint -W020, unused: false, noempty: false, boss: true */ /* * Implement localStorage to support Firefox 2-3 and IE 5-7 */ var localStorage; // jshint ignore:line { // test that Window.localStorage is available and works var windowLocalStorageAvailable = function windowLocalStorageAvailable() { var uid = new Date(); var result; try { window.localStorage.setItem(uid, uid); result = window.localStorage.getItem(uid) === String(uid); window.localStorage.removeItem(uid); return result; } catch (e) {// localStorage not available } return false; }; if (windowLocalStorageAvailable()) { localStorage = window.localStorage; } else if (window.globalStorage) { // Firefox 2-3 use globalStorage // See https://developer.mozilla.org/en/dom/storage#globalStorage try { localStorage = window.globalStorage[window.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; } } if (!localStorage) { localStorage = { length: 0, setItem: function setItem(k, v) {}, getItem: function getItem(k) {}, removeItem: function removeItem(k) {}, clear: function clear() {}, key: function key(k) {} }; } } var localStorage$1 = localStorage; /* jshint -W020, unused: false, noempty: false, boss: true */ var cookieStorage = function cookieStorage() { this.storage = null; }; cookieStorage.prototype.getStorage = function () { if (this.storage !== null) { return this.storage; } if (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 || window && window.location && window.location.hostname; return this._options.secure = opts.secure || false; }, get: function get(name) { try { return JSON.parse(localStorage$1.getItem(keyPrefix + name)); } catch (e) {} return null; }, set: function set(name, value) { try { localStorage$1.setItem(keyPrefix + name, JSON.stringify(value)); return true; } catch (e) {} return false; }, remove: function remove(name) { try { localStorage$1.removeItem(keyPrefix + name); } catch (e) { return false; } } }; } return this.storage; }; /** * MetadataStorage involves SDK data persistance * storage priority: cookies -> localStorage -> in memory * 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; _classCallCheck(this, MetadataStorage); this.storageKey = storageKey; this.domain = domain; this.secure = secure; this.sameSite = sameSite; this.expirationDays = expirationDays; this.cookieDomain = ''; { var writableTopDomain = topDomain(getLocation().href); this.cookieDomain = domain || (writableTopDomain ? '.' + writableTopDomain : null); } this.disableCookieStorage = disableCookies || !baseCookie.areCookiesEnabled({ domain: this.cookieDomain, secure: this.secure, sameSite: this.sameSite, expirationDays: this.expirationDays }); } _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; 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('.'); if (this.disableCookieStorage) { localStorage$1.setItem(this.storageKey, value); } else { baseCookie.set(this.getCookieStorageKey(), value, { domain: this.cookieDomain, secure: this.secure, sameSite: this.sameSite, expirationDays: this.expirationDays }); } } }, { key: "load", value: function load() { var str; if (!this.disableCookieStorage) { str = baseCookie.get(this.getCookieStorageKey() + '='); } if (!str) { str = localStorage$1.getItem(this.storageKey); } if (!str) { return null; } var values = str.split('.'); var userId = null; if (values[1]) { try { userId = Base64.decode(values[1]); } catch (e) { userId = null; } } return { deviceId: values[0], userId: userId, optOut: values[2] === '1', sessionId: parseInt(values[3], 32), lastEventTime: parseInt(values[4], 32), eventId: parseInt(values[5], 32), identifyId: parseInt(values[6], 32), sequenceNumber: parseInt(values[7], 32) }; } }]); 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'; /** * 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 (!this.userPropertiesOperations.hasOwnProperty(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_type').unset('age'); * amplitude.identify(identify); // send the Identify call */ Identify.prototype.unset = function (property) { this._addOperation(AMP_OP_UNSET, property, '-'); return this; }; /** * Helper function that adds operation to the Identify's object * Handle's filtering of duplicate user property keys, and filtering for clearAll. * @private */ Identify.prototype._addOperation = function (operation, property, value) { // check that the identify doesn't already contain a clearAll if (this.userPropertiesOperations.hasOwnProperty(AMP_OP_CLEAR_ALL)) { utils.log.error('This identify already contains a $clearAll operation, skipping operation ' + operation); return; } // check that property wasn't already used in this Identify if (this.properties.indexOf(property) !== -1) { utils.log.error('User property "' + property + '" already used in this identify, skipping operation ' + operation); return; } if (!this.userPropertiesOperations.hasOwnProperty(operation)) { this.userPropertiesOperations[operation] = {}; } this.userPropertiesOperations[operation][property] = value; this.properties.push(property); }; var commonjsGlobal = typeof window !== 'undefined' ? window : typeof global !== 'undefined' ? global : typeof self !== 'undefined' ? self : {}; function createCommonjsModule(fn, module) { return module = { exports: {} }, fn(module, module.exports), module.exports; } var md5 = createCommonjsModule(function (module) { (function ($) { /* * Add integers, wrapping at 2^32. This uses 16-bit operations internally * to work around bugs in some JS interpreters. */ function safeAdd (x, y) { var lsw = (x & 0xffff) + (y & 0xffff); var msw = (x >> 16) + (y >> 16) + (lsw >> 16); return (msw << 16) | (lsw & 0xffff) } /* * Bitwise rotate a 32-bit number to the left. */ function bitRotateLeft (num, cnt) { return (num << cnt) | (num >>> (32 - cnt)) } /* * These functions implement the four basic operations the algorithm uses. */ function md5cmn (q, a, b, x, s, t) { return safeAdd(bitRotateLeft(safeAdd(safeAdd(a, q), safeAdd(x, t)), s), b) } function md5ff (a, b, c, d, x, s, t) { return md5cmn((b & c) | (~b & d), a, b, x, s, t) } function md5gg (a, b, c, d, x, s, t) { return md5cmn((b & d) | (c & ~d), a, b, x, s, t) } function md5hh (a, b, c, d, x, s, t) { return md5cmn(b ^ c ^ d, a, b, x, s, t) } function md5ii (a, b, c, d, x, s, t) { return md5cmn(c ^ (b | ~d), a, b, x, s, t) } /* * Calculate the MD5 of an array of little-endian words, and a bit length. */ function binlMD5 (x, len) { /* append padding */ x[len >> 5] |= 0x80 << (len % 32); x[((len + 64) >>> 9 << 4) + 14] = len; var i; var olda; var oldb; var oldc; var oldd; var a = 1732584193; var b = -271733879; var c = -1732584194; var d = 271733878; for (i = 0; i < x.length; i += 16) { olda = a; oldb = b; oldc = c; oldd = d; a = md5ff(a, b, c, d, x[i], 7, -680876936); d = md5ff(d, a, b, c, x[i + 1], 12, -389564586); c = md5ff(c, d, a, b, x[i + 2], 17, 606105819); b = md5ff(b, c, d, a, x[i + 3], 22, -1044525330); a = md5ff(a, b, c, d, x[i + 4], 7, -176418897); d = md5ff(d, a, b, c, x[i + 5], 12, 1200080426); c = md5ff(c, d, a, b, x[i + 6], 17, -1473231341); b = md5ff(b, c, d, a, x[i + 7], 22, -45705983); a = md5ff(a, b, c, d, x[i + 8], 7, 1770035416); d = md5ff(d, a, b, c, x[i + 9], 12, -1958414417); c = md5ff(c, d, a, b, x[i + 10], 17, -42063); b = md5ff(b, c, d, a, x[i + 11], 22, -1990404162); a = md5ff(a, b, c, d, x[i + 12], 7, 1804603682); d = md5ff(d, a, b, c, x[i + 13], 12, -40341101); c = md5ff(c, d, a, b, x[i + 14], 17, -1502002290); b = md5ff(b, c, d, a, x[i + 15], 22, 1236535329); a = md5gg(a, b, c, d, x[i + 1], 5, -165796510); d = md5gg(d, a, b, c, x[i + 6], 9, -1069501632); c = md5gg(c, d, a, b, x[i + 11], 14, 643717713); b = md5gg(b, c, d, a, x[i], 20, -373897302); a = md5gg(a, b, c, d, x[i + 5], 5, -701558691); d = md5gg(d, a, b, c, x[i + 10], 9, 38016083); c = md5gg(c, d, a, b, x[i + 15], 14, -660478335); b = md5gg(b, c, d, a, x[i + 4], 20, -405537848); a = md5gg(a, b, c, d, x[i + 9], 5, 568446438); d = md5gg(d, a, b, c, x[i + 14], 9, -1019803690); c = md5gg(c, d, a, b, x[i + 3], 14, -187363961); b = md5gg(b, c, d, a, x[i + 8], 20, 1163531501); a = md5gg(a, b, c, d, x[i + 13], 5, -1444681467); d = md5gg(d, a, b, c, x[i + 2], 9, -51403784); c = md5gg(c, d, a, b, x[i + 7], 14, 1735328473); b = md5gg(b, c, d, a, x[i + 12], 20, -1926607734); a = md5hh(a, b, c, d, x[i + 5], 4, -378558); d = md5hh(d, a, b, c, x[i + 8], 11, -2022574463); c = md5hh(c, d, a, b, x[i + 11], 16, 1839030562); b = md5hh(b, c, d, a, x[i + 14], 23, -35309556); a = md5hh(a, b, c, d, x[i + 1], 4, -1530992060); d = md5hh(d, a, b, c, x[i + 4], 11, 1272893353); c = md5hh(c, d, a, b, x[i + 7], 16, -155497632); b = md5hh(b, c, d, a, x[i + 10], 23, -1094730640); a = md5hh(a, b, c, d, x[i + 13], 4, 681279174); d = md5hh(d, a, b, c, x[i], 11, -358537222); c = md5hh(c, d, a, b, x[i + 3], 16, -722521979); b = md5hh(b, c, d, a, x[i + 6], 23, 76029189); a = md5hh(a, b, c, d, x[i + 9], 4, -640364487); d = md5hh(d, a, b, c, x[i + 12], 11, -421815835); c = md5hh(c, d, a, b, x[i + 15], 16, 530742520); b = md5hh(b, c, d, a, x[i + 2], 23, -995338651); a = md5ii(a, b, c, d, x[i], 6, -198630844); d = md5ii(d, a, b, c, x[i + 7], 10, 1126891415); c = md5ii(c, d, a, b, x[i + 14], 15, -1416354905); b = md5ii(b, c, d, a, x[i + 5], 21, -57434055); a = md5ii(a, b, c, d, x[i + 12], 6, 1700485571); d = md5ii(d, a, b, c, x[i + 3], 10, -1894986606); c = md5ii(c, d, a, b, x[i + 10], 15, -1051523); b = md5ii(b, c, d, a, x[i + 1], 21, -2054922799); a = md5ii(a, b, c, d, x[i + 8], 6, 1873313359); d = md5ii(d, a, b, c, x[i + 15], 10, -30611744); c = md5ii(c, d, a, b, x[i + 6], 15, -1560198380); b = md5ii(b, c, d, a, x[i + 13], 21, 1309151649); a = md5ii(a, b, c, d, x[i + 4], 6, -145523070); d = md5ii(d, a, b, c, x[i + 11], 10, -1120210379); c = md5ii(c, d, a, b, x[i + 2], 15, 718787259); b = md5ii(b, c, d, a, x[i + 9], 21, -343485551); a = safeAdd(a, olda); b = safeAdd(b, oldb); c = safeAdd(c, oldc); d = safeAdd(d, oldd); } return [a, b, c, d] } /* * Convert an array of little-endian words to a string */ function binl2rstr (input) { var i; var output = ''; var length32 = input.length * 32; for (i = 0; i < length32; i += 8) { output += String.fromCharCode((input[i >> 5] >>> (i % 32)) & 0xff); } return output } /* * Convert a raw string to an array of little-endian words * Characters >255 have their high-byte silently ignored. */ function rstr2binl (input) { var i; var output = []; output[(input.length >> 2) - 1] = undefined; for (i = 0; i < output.length; i += 1) { output[i] = 0; } var length8 = input.length * 8; for (i = 0; i < length8; i += 8) { output[i >> 5] |= (input.charCodeAt(i / 8) & 0xff) << (i % 32); } return output } /* * Calculate the MD5 of a raw string */ function rstrMD5 (s) { return binl2rstr(binlMD5(rstr2binl(s), s.length * 8)) } /* * Calculate the HMAC-MD5, of a key and some data (raw strings) */ function rstrHMACMD5 (key, data) { var i; var bkey = rstr2binl(key); var ipad = []; var opad = []; var hash; ipad[15] = opad[15] = undefined; if (bkey.length > 16) { bkey = binlMD5(bkey, key.length * 8); } for (i = 0; i < 16; i += 1) { ipad[i] = bkey[i] ^ 0x36363636; opad[i] = bkey[i] ^ 0x5c5c5c5c; } hash = binlMD5(ipad.concat(rstr2binl(data)), 512 + data.length * 8); return binl2rstr(binlMD5(opad.concat(hash), 512 + 128)) } /* * Convert a raw string to a hex string */ function rstr2hex (input) { var hexTab = '0123456789abcdef'; var output = ''; var x; var i; for (i = 0; i < input.length; i += 1) { x = input.charCodeAt(i); output += hexTab.charAt((x >>> 4) & 0x0f) + hexTab.charAt(x & 0x0f); } return output } /* * Encode a string as utf-8 */ function str2rstrUTF8 (input) { return unescape(encodeURIComponent(input)) } /* * Take string arguments and return either raw or hex encoded strings */ function rawMD5 (s) { return rstrMD5(str2rstrUTF8(s)) } function hexMD5 (s) { return rstr2hex(rawMD5(s)) } function rawHMACMD5 (k, d) { return rstrHMACMD5(str2rstrUTF8(k), str2rstrUTF8(d)) } function hexHMACMD5 (k, d) { return rstr2hex(rawHMACMD5(k, d)) } function md5 (string, key, raw) { if (!key) { if (!raw) { return hexMD5(string) } return rawMD5(string) } if (!raw) { return hexHMACMD5(key, string) } return rawHMACMD5(key, string) } if (module.exports) { module.exports = md5; } else { $.md5 = md5; } })(commonjsGlobal); }); var strictUriEncode = function (str) { return encodeURIComponent(str).replace(/[!'()*]/g, function (c) { return '%' + c.charCodeAt(0).toString(16).toUpperCase(); }); }; /* object-assign (c) Sindre Sorhus @license MIT */ /* eslint-disable no-unused-vars */ var getOwnPropertySymbols = Object.getOwnPropertySymbols; var hasOwnProperty = Object.prototype.hasOwnProperty; var propIsEnumerable = Object.prototype.propertyIsEnumerable; function toObject(val) { if (val === null || val === undefined) { throw new TypeError('Object.assign cannot be called with null or undefined'); } return Object(val); } function shouldUseNative() { try { if (!Object.assign) { return false; } // Detect buggy property enumeration order in older V8 versions. // https://bugs.chromium.org/p/v8/issues/detail?id=4118 var test1 = new String('abc'); // eslint-disable-line no-new-wrappers test1[5] = 'de'; if (Object.getOwnPropertyNames(test1)[0] =