UNPKG

begrowth-analytics-utils

Version:

Analytics utility functions used by 'analytics' module

898 lines (727 loc) 23.6 kB
var analyticsUtils = (function (exports) { 'use strict'; function dlv_es(t,e,l,n,r){for(e=e.split?e.split("."):e,n=0;n<e.length;n++)t=t?t[e[n]]:r;return t===r?l:t} /* // set cookie('test', 'a') // complex set - cookie(name, value, ttl, path, domain, secure) cookie('test', 'a', 60*60*24, '/api', '*.example.com', true) // get cookie('test') // destroy cookie('test', '', -1) */ function cookie(name, value, ttl, path, domain, secure) { if (typeof window === 'undefined') return; /* Set values */ if (arguments.length > 1) { // eslint-disable-next-line no-return-assign return document.cookie = name + '=' + encodeURIComponent(value) + (!ttl ? '' : // Has TTL set expiration on cookie '; expires=' + new Date(+new Date() + ttl * 1000).toUTCString() + (!path ? '' : '; path=' + path) + (!domain ? '' : '; domain=' + domain) + (!secure ? '' : '; secure')); } return decodeURIComponent((('; ' + document.cookie).split('; ' + name + '=')[1] || '').split(';')[0]); } function hasCookieSupport() { try { var key = '_c_'; // Try to set cookie cookie(key, '1'); var valueSet = document.cookie.indexOf(key) !== -1; // Cleanup cookie cookie(key, '', -1); return valueSet; } catch (e) { return false; } } /** * Get a cookie value * @param {string} name - key of cookie * @return {string} value of cookie */ var getCookie = cookie; /** * Set a cookie value * @param {string} name - key of cookie * @param {string} value - value of cookie * @param {string} days - days to keep cookie */ var setCookie = cookie; /** * Remove a cookie value. * @param {string} name - key of cookie */ function removeCookie(name) { return cookie(name, '', -1); } function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); if (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 = arguments[i] != null ? arguments[i] : {}; if (i % 2) { ownKeys(Object(source), true).forEach(function (key) { _defineProperty(target, key, source[key]); }); } else if (Object.getOwnPropertyDescriptors) { Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)); } else { ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } } return target; } function _typeof(obj) { "@babel/helpers - typeof"; 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 _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 hasLocalStorage() { return false; } function parse(input) { var value; try { value = JSON.parse(input); if (typeof value === 'undefined') { value = input; } if (value === 'true') { value = true; } if (value === 'false') { value = false; } if (parseFloat(value) === value && _typeof(value) !== 'object') { value = parseFloat(value); } } catch (e) { value = input; } return value; } var globalContext = (typeof self === "undefined" ? "undefined" : _typeof(self)) === 'object' && self.self === self && self || (typeof global === "undefined" ? "undefined" : _typeof(global)) === 'object' && global.global === global && global || undefined; var ALL = '*'; var LOCAL_STORAGE = 'localStorage'; var COOKIE = 'cookie'; var GLOBAL = 'global'; // Verify support var hasStorage = hasLocalStorage(); var hasCookies = hasCookieSupport(); /** * Get storage item from localStorage, cookie, or window * @param {string} key - key of item to get * @param {object|string} [options] - storage options. If string location of where to get storage * @param {string} [options.storage] - Define type of storage to pull from. * @return {Any} the value of key */ function getItem(key) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!key) return null; var storageType = getStorageType(options); // Get value from all locations if (storageType === ALL) return getAll(key); /* 2. Fallback to cookie */ if (useCookie(storageType)) { var _value = getCookie(key); if (_value || storageType === COOKIE) return parse(_value); } /* 3. Fallback to window/global. */ return globalContext[key] || null; } function getAll(key) { return { cookie: parse(getCookie(key)), localStorage: parse(localStorage.getItem(key)), global: globalContext[key] || null }; } /** * Store values in localStorage, cookie, or window * @param {string} key - key of item to set * @param {*} value - value of item to set * @param {object|string} [options] - storage options. If string location of where to get storage * @param {string} [options.storage] - Define type of storage to pull from. * @returns {object} returns old value, new values, & location of storage */ function setItem(key, value) { var options = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {}; if (!key || typeof value === 'undefined') { return; } var data = {}; var storageType = getStorageType(options); var saveValue = JSON.stringify(value); var setAll = storageType === ALL; /* 2. Fallback to cookie */ if (useCookie(storageType)) { // console.log('SET as cookie', saveValue) var cookieValues = { current: value, previous: parse(getCookie(key)) }; // Set Cookie setCookie(key, saveValue); if (!setAll) { return _objectSpread2({ location: COOKIE }, cookieValues); } // Set object data[COOKIE] = cookieValues; } /* 3. Fallback to window/global */ var globalValues = { current: value, previous: globalContext[key] }; // Set global value globalContext[key] = value; if (!setAll) { return _objectSpread2({ location: GLOBAL }, globalValues); } // Set object data[GLOBAL] = globalValues; return data; } /** * Remove values from localStorage, cookie, or window * @param {string} key - key of item to set * @param {object|string} [options] - storage options. If string location of where to get storage * @param {string} [options.storage] - Define type of storage to pull from. */ function removeItem(key) { var options = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; if (!key) return false; var storageType = getStorageType(options); var removeAll = storageType === ALL; var locations = []; if (removeAll || useLocal()) { /* 1. Try localStorage */ localStorage.removeItem(key); locations.push(LOCAL_STORAGE); } if (removeAll || useCookie(storageType)) { /* 2. Fallback to cookie */ removeCookie(key); locations.push(COOKIE); } /* 3. Fallback to window/global */ if (removeAll || useGlobal(storageType)) { globalContext[key] = undefined; locations.push(GLOBAL); } return locations; } function getStorageType(options) { return typeof options === 'string' ? options : options.storage; } function useGlobal(storage) { return !storage || storage === GLOBAL; } function useLocal(storage) { // If has localStorage and storage option not defined, or is set to 'localStorage' or '*' return hasStorage ; } function useCookie(storage) { // If has cookies and storage option not defined, or is set to 'cookies' or '*' return hasCookies && (!storage || storage === COOKIE || storage === ALL); } var index = { getItem: getItem, setItem: setItem, removeItem: removeItem }; function _typeof$1(obj) { "@babel/helpers - typeof"; if (typeof Symbol === "function" && typeof Symbol.iterator === "symbol") { _typeof$1 = function (obj) { return typeof obj; }; } else { _typeof$1 = function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; } return _typeof$1(obj); } function isFunction(x) { return typeof x === 'function'; } /** * @param x * @return {x is string} */ function isString(x) { return typeof x === 'string'; } /** * @param x * @return {x is undefined} */ function isUndefined(x) { return typeof x === 'undefined'; } /** * @param x * @return {x is boolean} */ function isBoolean(x) { return typeof x === 'boolean'; } /** * @template T * @param x * @return {x is Array<T>} */ function isArray(x) { return Array.isArray(x); } /** * @param obj * @return {obj is Object} */ function isObject(obj) { if (_typeof$1(obj) !== 'object' || obj === null) return false; var proto = obj; while (Object.getPrototypeOf(proto) !== null) { proto = Object.getPrototypeOf(proto); } return Object.getPrototypeOf(obj) === proto; } function decode(s) { try { return decodeURIComponent(s.replace(/\+/g, ' ')); } catch (e) { return null; } } var inBrowser = typeof document !== 'undefined'; /** * @returns {string | undefined} */ function getBrowserLocale() { if (!inBrowser) return; var _navigator = navigator, language = _navigator.language, languages = _navigator.languages, userLanguage = _navigator.userLanguage; if (userLanguage) return userLanguage; // IE only return languages && languages.length ? languages[0] : language; } function getTimeZone() { try { return Intl.DateTimeFormat().resolvedOptions().timeZone; } catch (error) {} } /** * @param {string | null | undefined} ref * @returns {boolean | undefined} */ function isExternalReferrer(ref) { if (!inBrowser) return false; var referrer = ref || document.referrer; if (referrer) { var port = window.document.location.port; var _ref = referrer.split('/')[2]; if (port) { _ref = _ref.replace(":".concat(port), ''); } return _ref !== window.location.hostname; } return false; } /** * Check if a script is loaded * @param {String|RegExp} script - Script src as string or regex * @return {Boolean} is script loaded */ function isScriptLoaded(script) { if (!inBrowser) return true; var scripts = document.getElementsByTagName('script'); return !!Object.keys(scripts).filter(function (key) { var src = scripts[key].src; if (typeof script === 'string') { return src.indexOf(script) !== -1; } else if (script instanceof RegExp) { return src.match(script); } return false; }).length; } function noOp() {} function paramsClean(url, param) { var search = (url.split('?') || [,])[1]; // eslint-disable-line if (!search || search.indexOf(param) === -1) { return url; } // remove all utm params from URL search var regex = new RegExp("(\\&|\\?)".concat(param, "([_A-Za-z0-9\"+=.\\/\\-@%]+)"), 'g'); var cleanSearch = "?".concat(search).replace(regex, '').replace(/^&/, '?'); // replace search params with clean params var cleanURL = url.replace("?".concat(search), cleanSearch); // use browser history API to clean the params return cleanURL; } /** * Get a given query parameter value * @param {string} param - Key of parameter to find * @param {string} url - url to search * @return {string} match */ function getValueParamValue(param, url) { return decode((RegExp("".concat(param, "=(.+?)(&|$)")).exec(url) || [, ''])[1]); } /** * Get search string from given url * @param {string} [url] - optional url string. If no url, window.location.search will be used * @return {string} url search string */ function getSearchString(url) { if (url) { var p = url.match(/\?(.*)/); return p && p[1] ? p[1].split('#')[0] : ''; } return inBrowser && window.location.search.substring(1); } /** * Parse url parameters into javascript object * @param {string} [url] - URI to parse. If no url supplied window.location will be used * @return {object} parsed url parameters */ function paramsParse(url) { return getParamsAsObject(getSearchString(url)); } /* ?first=abc&a[]=123&a[]=false&b[]=str&c[]=3.5&a[]=last https://random.url.com?Target=Report&Method=getStats&fields%5B%5D=Offer.name&fields%5B%5D=Advertiser.company&fields%5B%5D=Stat.clicks&fields%5B%5D=Stat.conversions&fields%5B%5D=Stat.cpa&fields%5B%5D=Stat.payout&fields%5B%5D=Stat.date&fields%5B%5D=Stat.offer_id&fields%5B%5D=Affiliate.company&groups%5B%5D=Stat.offer_id&groups%5B%5D=Stat.date&filters%5BStat.affiliate_id%5D%5Bconditional%5D=EQUAL_TO&filters%5BStat.affiliate_id%5D%5Bvalues%5D=1831&limit=9999 https://random.url.com?Target=Offer&Method=findAll&filters%5Bhas_goals_enabled%5D%5BTRUE%5D=1&filters%5Bstatus%5D=active&fields%5B%5D=id&fields%5B%5D=name&fields%5B%5D=default_goal_name http://localhost:3000/?Target=Offer&Method=findAll&filters[has_goals_enabled][TRUE]=1&filters[status]=active&filters[wow]arr[]=yaz&filters[wow]arr[]=naz&fields[]=id&fields[]=name&fields[]=default_goal_name */ function getParamsAsObject(query) { var params = {}; var temp; var re = /([^&=]+)=?([^&]*)/g; while (temp = re.exec(query)) { var k = decode(temp[1]); var v = decode(temp[2]); if (k.substring(k.length - 2) === '[]') { k = k.substring(0, k.length - 2); (params[k] || (params[k] = [])).push(v); } else { params[k] = v === '' ? true : v; } } for (var prop in params) { var arr = prop.split('['); if (arr.length > 1) { assign(params, arr.map(function (x) { return x.replace(/[?[\]\\ ]/g, ''); }), params[prop]); delete params[prop]; } } return params; } function assign(obj, keyPath, value) { var lastKeyIndex = keyPath.length - 1; for (var i = 0; i < lastKeyIndex; ++i) { var key = keyPath[i]; if (!(key in obj)) { obj[key] = {}; } obj = obj[key]; } obj[keyPath[lastKeyIndex]] = value; } /* https://github.com/choojs/nanoquery/blob/791cbdfe49cc380f0b2f93477572128946171b46/browser.js var reg = /([^?=&]+)(=([^&]*))?/g function qs (url) { var obj = {} url.replace(/^.*\?/, '').replace(reg, function (a0, a1, a2, a3) { var value = decodeURIComponent(a3) var key = decodeURIComponent(a1) if (obj.hasOwnProperty(key)) { if (Array.isArray(obj[key])) obj[key].push(value) else obj[key] = [obj[key], value] } else { obj[key] = value } }) return obj } */ /** * Removes params from url in browser * @param {string} param - param key to remove from current URL * @param {() => void} [callback] - callback function to run. Only runs in browser * @return {PromiseLike<void>} */ function paramsRemove(param, callback) { if (!inBrowser) return Promise.resolve(); return new Promise(function (resolve, reject) { if (window.history && window.history.replaceState) { var url = window.location.href; var cleanUrl = paramsClean(url, param); if (url !== cleanUrl) { /* replace URL with history API */ // eslint-disable-next-line no-restricted-globals history.replaceState({}, '', cleanUrl); } } if (callback) callback(); return resolve(); }); } /** * Get host domain of url * @param {String} url - href of page * @return {String} hostname of page * * @example * getDomainHost('https://subdomain.my-site.com/') * > subdomain.my-site.com */ function getDomainHost(url) { if (!inBrowser) return null; var a = document.createElement('a'); a.setAttribute('href', url); return a.hostname; } /** * Get host domain of url * @param {String} url - href of page * @return {String} base hostname of page * * @example * getDomainBase('https://subdomain.my-site.com/') * > my-site.com */ function getDomainBase(url) { var host = getDomainHost(url) || ''; return host.split('.').slice(-2).join('.'); } /** * Remove TLD from domain string * @param {String} baseDomain - host name of site * @return {String} * @example * trimTld('google.com') * > google */ function trimTld(baseDomain) { var arr = baseDomain.split('.'); return arr.length > 1 ? arr.slice(0, -1).join('.') : baseDomain; } var url = { trimTld: trimTld, getDomainBase: getDomainBase, getDomainHost: getDomainHost }; var googleKey = 'google'; /** * @typedef {{ * campaign: string, * referrer?: string, * } & DomainObject & Object.<string, any>} ReferrerObject */ /** * Checks a given url and parses referrer data * @param {String} [referrer] - (optional) referring URL * @param {String} [currentUrl] - (optional) the current url * @return {ReferrerObject} [description] */ function parseReferrer(referrer, currentUrl) { if (!inBrowser) return false; // default referral data var refData = { 'source': '(direct)', 'medium': '(direct)', 'campaign': '(direct)' }; // Add raw ref url if external if (referrer && isExternalReferrer(referrer)) { refData.referrer = referrer; } var domainInfo = parseDomain(referrer); // Read referrer URI and infer source if (domainInfo && Object.keys(domainInfo).length) { refData = Object.assign({}, refData, domainInfo); } // Read URI params and use set utm params var params = paramsParse(currentUrl); var paramKeys = Object.keys(params); if (!paramKeys.length) { return refData; } // set campaign params off GA matches var gaParams = paramKeys.reduce(function (acc, key) { // match utm params & dclid (display) & gclid (cpc) if (key.match(/^utm_/)) { acc["".concat(key.replace(/^utm_/, ''))] = params[key]; } // https://developers.google.com/analytics/devguides/collection/protocol/v1/parameters // dclid - cpc Cost-Per-Thousand Impressions // gclid - cpc Cost per Click if (key.match(/^(d|g)clid/)) { acc['source'] = googleKey; acc['medium'] = params.gclid ? 'cpc' : 'cpm'; acc[key] = params[key]; } return acc; }, {}); return Object.assign({}, refData, gaParams); } /** * @typedef {{ * source: string, * medium: string, * term?: string * }} DomainObject */ /** * Client side domain parser for determining marketing data. * @param {String} referrer - ref url * @return {DomainObject | boolean} */ function parseDomain(referrer) { if (!referrer || !inBrowser) return false; var referringDomain = getDomainBase(referrer); var a = document.createElement('a'); a.href = referrer; // Shim for the billion google search engines if (a.hostname.indexOf(googleKey) > -1) { referringDomain = googleKey; } // If is search engine if (searchEngines[referringDomain]) { var searchEngine = searchEngines[referringDomain]; var queryParam = typeof searchEngine === 'string' ? searchEngine : searchEngine.p; var termRegex = new RegExp(queryParam + '=.*?([^&#]*|$)', 'gi'); var term = a.search.match(termRegex); return { source: searchEngine.n || trimTld(referringDomain), medium: 'direct', term: (term ? term[0].split('=')[1] : '') || '(not provided)' }; } // Default var medium = !isExternalReferrer(referrer) ? 'direct' : 'referral'; return { source: a.hostname, medium: medium }; } /** * Search engine query string data * @type {Object} */ var Q = 'q'; var QUERY = 'query'; var searchEngines = { 'daum.net': Q, 'eniro.se': 'search_word', 'naver.com': QUERY, 'yahoo.com': 'p', 'msn.com': Q, 'aol.com': Q, 'ask.com': Q, 'baidu.com': 'wd', 'yandex.com': 'text', 'rambler.ru': 'words', 'google': Q, 'bing.com': { 'p': Q, 'n': 'live' } }; /** * @return {string} */ function uuid() { var u = '', m = 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', i = 0, rb = Math.random() * 0xffffffff | 0; while (i++ < 36) { var c = m[i - 1], r = rb & 0xf, v = c == 'x' ? r : r & 0x3 | 0x8; u += c == '-' || c == '4' ? c : v.toString(16); rb = i % 8 == 0 ? Math.random() * 0xffffffff | 0 : rb >> 4; } return u; } function throttle(func, wait) { var context, args, result; var timeout = null; var previous = 0; var later = function later() { previous = new Date(); timeout = null; result = func.apply(context, args); }; return function () { var now = new Date(); if (!previous) { previous = now; } var remaining = wait - (now - previous); context = this; args = arguments; if (remaining <= 0) { clearTimeout(timeout); timeout = null; previous = now; result = func.apply(context, args); } else if (!timeout) { timeout = setTimeout(later, remaining); } return result; }; } exports.decodeUri = decode; exports.dotProp = dlv_es; exports.getBrowserLocale = getBrowserLocale; exports.getCookie = getCookie; exports.getTimeZone = getTimeZone; exports.globalContext = globalContext; exports.inBrowser = inBrowser; exports.isArray = isArray; exports.isBoolean = isBoolean; exports.isExternalReferrer = isExternalReferrer; exports.isFunction = isFunction; exports.isObject = isObject; exports.isScriptLoaded = isScriptLoaded; exports.isString = isString; exports.isUndefined = isUndefined; exports.noOp = noOp; exports.paramsClean = paramsClean; exports.paramsGet = getValueParamValue; exports.paramsParse = paramsParse; exports.paramsRemove = paramsRemove; exports.parseReferrer = parseReferrer; exports.removeCookie = removeCookie; exports.setCookie = setCookie; exports.storage = index; exports.throttle = throttle; exports.url = url; exports.uuid = uuid; return exports; }({}));