begrowth-analytics-utils
Version:
Analytics utility functions used by 'analytics' module
898 lines (727 loc) • 23.6 kB
JavaScript
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;
}({}));