@ampush/centaurus
Version:
Centaurus, is an Ampush repository designed to house common UI components, JS classes, templates and API methods in a central place that can be imported and reused across other Ampush partner repositories as needed.
161 lines (151 loc) • 6.39 kB
JavaScript
const getCookie = (utmKey) => {
const cookieString = decodeURIComponent(document.cookie).split('; ');
for (let val = 0; val < cookieString.length; val++) {
const item = cookieString[val];
const queryVal = item.split(`${utmKey}=`)[1];
const queryKey = item.split('=')[0];
if (utmKey === queryKey) {
return queryVal;
}
}
return '';
};
const { hostname } = window.location;
const [subDomain, domainName, top] = hostname.split('.');
const host = hostname;
const isLocalhost = hostname.indexOf('localhost') !== -1 || hostname.indexOf('0.0.0.0') !== -1;
const parsedDomain = isLocalhost ? host : domainName ? `${domainName}.${top || 'com'}` : '';
const parsedSubDomain = isLocalhost ? '' : subDomain;
const hostName = isLocalhost
? `http://${host}:${window.location.port}`
: `https://${parsedSubDomain}.${parsedDomain}`;
const removeDuplicates = (str, splitChar) => {
const splitArr = str.split(splitChar);
const obj = {};
splitArr.forEach((v) => {
const [key, value] = v.split('=');
obj[key] = value;
});
const keys = Object.keys(obj);
return keys.map((k) => `${k}=${obj[k]}`).join(splitChar);
};
const color = 'color:#008b8b';
const background = 'background-color:#fff8dc';
const whiteSpace = 'white-space:break-spaces';
const display = 'display:flow';
const padding = 'padding: 0 30px';
const textAlign = 'text-align: center';
const styles = [color, background, whiteSpace, display, padding, textAlign];
const logCss = styles.join(';');
const redirect = (variant, utmExp, additionalUtms) => {
const { url, name } = variant;
const qsToPass = window.location.search.substr(1) !== '' ? `${removeDuplicates(window.location.search.substr(1), '&')}` : '';
const baseUrl = url.indexOf('.') !== -1 ? `https://${url}` : `${hostName}/${url}`;
let fullUrl = qsToPass ? `${baseUrl}?${qsToPass}` : baseUrl;
if (name) {
const urlPart = [utmExp, name].filter((param) => param).join('');
const cleanParams = [qsToPass, urlPart].filter((param) => param).join('&');
const params = removeDuplicates(cleanParams, '&');
fullUrl = `${baseUrl}?${params}`;
}
console.info('%c __ Redirecting to __ ', logCss);
console.info(JSON.stringify({ fullUrl, variant }, null, 2));
const operand = fullUrl.indexOf('?') !== -1 ? '&' : '?';
document.location.replace(fullUrl.concat(additionalUtms ? `${operand}${additionalUtms}` : ''));
};
const setCookie = (cookieStr, doExpires) => {
if (doExpires) {
const date = new Date();
date.setTime(date.getTime() + 365 * 24 * 60 * 60 * 1000);
const expires = `expires=${date.toUTCString()}`;
document.cookie = `${cookieStr};${expires};domain=${parsedDomain};path=/`;
}
else {
document.cookie = `${cookieStr};domain=${parsedDomain};path=/`;
}
};
const compare = (a = 0, b = 0) => {
let comparison = 0;
if (a < b) {
comparison = 1;
}
else if (a > b) {
comparison = -1;
}
return comparison;
};
function Split(experience, experimentName) {
const { cookies = [], homeExpCookie = 'utm_home_exp', variants = [], additionalUtms = '' } = experience;
if (!variants.length) {
throw Error('No variants supplied, please re-visit split config!');
}
if (!experimentName) {
throw Error('No experimentName passed for naming the experiment name!');
}
variants.forEach(({ name, url }, index) => {
if (!url || !name) {
throw Error(`Missing url for variant at index ${index} | name: ${name} | url: ${url}`);
}
});
const splitArray = [];
const variantsWithoutPercentages = variants.filter(({ percentage }) => !percentage);
const variantsWithPercentages = variants.filter(({ percentage }) => percentage);
const reducePercentages = variantsWithPercentages.reduce((sum, { percentage }) => sum + percentage, 0);
const splitPercentage = (100 - (reducePercentages || 0)) / (variantsWithoutPercentages.length || 1);
variants.sort((a, b) => compare(a.percentage, b.percentage));
variants.forEach(({ name, percentage }) => {
const loop = percentage || splitPercentage;
for (let index = 0; index < loop; index++) {
splitArray.push(name);
}
});
const splitResult = Math.floor(Math.random() * 100);
const answerName = splitArray[splitResult];
const answerObj = variants.filter(({ name }) => name === answerName)[0] || {};
setCookie(`${homeExpCookie}=${experimentName}${answerObj['name']}`, false);
setCookie(`${homeExpCookie}_url=${answerObj['url']}`, false);
cookies.forEach(({ key, val }) => {
setCookie(`${key}=${val}`);
});
console.info('%c ·· Split result /Y\\ ·· ', logCss);
console.info(JSON.stringify({
splitArray,
answerObj,
splitResult,
additionalUtms,
}, null, 2));
return {
answerObj,
splitResult,
additionalUtms,
};
}
const Plover = (utmExpName, experiences) => {
const { tests, revisitingUser, testName } = experiences;
if (revisitingUser) {
const numRegex = /\d+/;
const [utmTestName] = utmExpName.split('=');
const revisitingCookie = getCookie(utmTestName);
const [revisitingExp, variantName] = revisitingCookie.split('_');
const revisitingTestNumber = (revisitingExp.match(numRegex) || ['1'])[0];
const revisitingTestName = revisitingExp.split(revisitingTestNumber)[0];
const returingExpName = `${utmTestName}=${revisitingTestName}${revisitingTestNumber}_`;
const variantUrl = getCookie(`${utmTestName}_url`);
const revisitExperience = {
name: variantName,
url: variantUrl,
};
console.info('%c Revisiting User', logCss);
console.info(JSON.stringify(revisitExperience, null, 2));
return redirect(revisitExperience, returingExpName);
}
const experience = (tests && tests.filter(({ rule }) => rule)[0]) || experiences.default;
if (experience) {
const { answerObj, additionalUtms } = Split(experience, testName);
redirect(answerObj, utmExpName, additionalUtms);
}
else {
console.error('ERROR! No experience matched on rules. Set one to "true"');
}
};
export default Plover;