Skip to content

Instantly share code, notes, and snippets.

@seanemmel-ba
Last active February 12, 2019 15:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save seanemmel-ba/192d7ba425d8f6a51d00 to your computer and use it in GitHub Desktop.
Save seanemmel-ba/192d7ba425d8f6a51d00 to your computer and use it in GitHub Desktop.
A bookmarklet that enables you to retrieve information about Qubit experiments, including IDs, variations mapping, creative key groups, SmartServe script ID, and more, all in a GUI modal.
javascript:(function() {
/**
* @package N/A
* @version 1.1 //=> 3/1/2016
* @author Blue Acorn <code@blueacorn.com>, Sean Emmel <sean.emmel@blueacorn.com>
* @copyright Copyright © 2016 Blue Acorn.
*
* LAST UPDATED: 3/1/2016
@FIXES:
* Fixing issue where you were shown to be in the Control when actually it was a
variation and vice versa
* No longer able to pull the variation index as this was based on erroneous data
* Rewording results for full experiment info to reflect above changes
@TODO:
* Find out if there's a way to determine variation index
* Determine what a value of 0 means for exp.ckg property (multivariate usage)
*
* Errors? Email me: <sean.emmel@blueacorn.com>
*/
/* Quick check to ensure Qubit is loaded on the page */
if (!window._qb_ss || !window.qb_etc_data) {
alert('Qubit is not on page OR Qubit global variable dependencies not loaded!');
return;
}
var ALL_EXP = window._qb_ss[0].experiments;
var CURR_EXP = window.qb_etc_data;
var DOC = window.document;
var HEAD = DOC.getElementsByTagName('head')[0];
var BODY = DOC.body;
function Modal() {
this.el = document.createElement('div');
this.el.id = 'qubit-modal-overlay';
this.styles = '#qubit-modal-overlay { position: fixed; top: 0; left: 0; width: 100%; height: 100%; z-index: 10000000000; background-color: rgba(180, 180, 180, 0.6); overflow-y: auto; } #qubit-modal-container { position: relative; margin: 8% auto; width: 500px; padding: 20px; z-index: 100000000000; background-color: #4c4f53; border-radius: 2px; box-shadow: 1px 1px 5px 2px #444; font-family: \'Arial\', sans-serif; text-align: left; font-size: 16px; } #qubit-modal-close-x { position: absolute; top: -30px; right: -25px; width: 30px; height: 30px; background-image: url(\'//cdn.optimizely.com/img/543470256/b2d11caa90f5463e9d8cbae950656144.png\'); background-repeat: no-repeat; background-size: 30px 30px; } #qubit-modal-close-x:hover { cursor: pointer; } #qubit-header { margin-bottom: 20px; padding: 10px 0; color: #fff; background-color: #f9b03d; font-weight: bold; font-size: 22px; text-align: center; } #qubit-options h3 { display: block; margin: 20px 0 12px 0; color: #fff; font-weight: bold; font-size: 20px; font-family: \'Arial\', sans-serif; letter-spacing: 0; } #qubit-options label { display: inline-block; width: 100%; margin-bottom: 10px; color: #fff; font-size: 18px; font-weight: bold; letter-spacing: 0; transition: all 0.1s ease-in-out; } #qubit-options label:hover { cursor: pointer; color: #5d9fd5; text-shadow: 1px 1px 2px #000; } #qubit-options input[type=\"checkbox\"] { display:none; } #qubit-options input[type=\"checkbox\"] + label span { display:inline-block; width:19px; height:19px; margin:-1px 4px 0 0; vertical-align:middle; background-image: url(\'http://cdn.optimizely.com/img/404692387/9fd5ca6857104cbebb2502b34a49bc66.png\'); background-position: left top; background-repeat: no-repeat; cursor:pointer; } #qubit-options input[type=\"checkbox\"]:checked + label { color: #5d9fd5; } #qubit-options input[type=\"checkbox\"]:checked + label span { background-image: url(\'http://cdn.optimizely.com/img/404692387/9fd5ca6857104cbebb2502b34a49bc66.png\'); background-position: -19px top; background-repeat: no-repeat; } #qubit-options .note { margin-top: 5px; font-style: italic; font-size: 14px; color: #fff; } #qubit-modal-container .center-me { text-align: center; } #qubit-go { width: 150px; height: 50px; margin: 10px auto 0 auto; border: 2px solid #48b28d; border-radius: 5px; background-color: #48b28d; color: #fff; letter-spacing: 3px; font-size: 20px; transition: all 0.2s ease-in-out; } #qubit-go:hover { cursor: pointer; background-color: #249780; border-color: #249780; transform: scale(1.1); } #qubit-help { margin-top: 5px; padding: 10px 5px 0 5px; letter-spacing: 0; } #qubit-help::after { content: \'\'; display: block; width: 100%; clear: both; } #qubit-help a { color: #e2e2e2; text-decoration: none; letter-spacing: 0; transition: all 0.2s ease-in-out; } #qubit-help a:hover { color: #fff; transform: translateY(1px) scale(1.1); } #qubit-help a:first-child { float: left; } #qubit-help a:last-child { float: right; } #qubit-close-modal { width: 150px; height: 50px; margin: 10px auto 0 auto; border: 1px solid #fff; border-radius: 2px; background-color: #4c4f53; color: #fff; letter-spacing: 3px; font-size: 20px; transition: all 0.2s ease-in-out; } #qubit-close-modal:hover { cursor: pointer; background-color: #fff; color: #4c4f53; transform: scale(1.1); } #qubit-results, #qubit-help, #qubit-close-modal-wrap { display: none; } #qubit-results { max-height: 350px; margin-top: 20px; padding: 10px; background-color: #B3AFAF; border: 1px solid #e2e2e2; border-radius: 2px; overflow-y: auto; } #qubit-results .row { padding: 8px 0; border-bottom: 1px solid #eee; color: #000; } #qubit-results .title { margin-bottom: 5px; font-weight: bold; letter-spacing: 0; color: #000; } #qubit-results .value { margin-bottom: 10px; padding-bottom: 10px; word-wrap: break-word; letter-spacing: 0; color: #000; } #qubit-results strong { font-weight: bold; color: #000; }';
}
Modal.prototype = {
checkQubitVarsLoaded: function() {
if (ALL_EXP === null || ALL_EXP === undefined || CURR_EXP === null || CURR_EXP === undefined) {
alert('Qubit is not on page OR Qubit global variable dependencies not loaded!');
return false;
}
return true;
},
buildDivRow: function(html) {
var div = document.createElement('div');
div.setAttribute('class', 'row');
div.innerHTML = html;
return div;
},
getSmartServeScriptId: function() {
var script = document.querySelector('script[src*="smartserve"]');
var id = script.src.match(/\b\d+\b/g)[0]; /* currently works for both regular smartserve AND preview script versions */
var html = '<strong>SmartServe Script ID: </strong>' + id;
return this.buildDivRow(html);
},
getExperimentVariationMapByIndex: function(exp) {
var index = exp.cnt === 1 ? 'Control (0) ' : 'Variation X';
var html = '<strong>Experiment ID: </strong>' + exp.e + ' :: <strong>Variation Index: </strong>' + index;
return this.buildDivRow(html);
},
getExperimentVariationMapById: function(exp) {
var id = exp.cm;
var html = '<strong>Experiment ID: </strong>' + exp.e + ' :: <strong>Variation ID: </strong>' + id;
return this.buildDivRow(html);
},
getMultivariateCreativeKeyGroup: function(exp) {
var ckg = exp.ckg; /* What does 0 mean here? Does that mean not a multivariate? */
var html = '<strong>Experiment ID: </strong>' + exp.e + ' :: <strong>Variation Creative Key Group: </strong>' + ckg;
return this.buildDivRow(html);
},
getExperimentFullInfo: function(exp) {
var index = exp.cnt === 1 ? 'Control (0) ' : 'X';
var html =
'<strong>Experiment ID: </strong>' + exp.e + ' :: ' +
'<strong>Iteration ID: </strong>' + exp.i + ' :: ' +
'<strong>Active Variation ID: </strong>' + exp.cm + ' :: ' +
'<strong>Active Variation Index: </strong>' + index + ' :: ' +
'<strong>Active Variation Creative Key Group: </strong>' + exp.ckg + ' :: ' +
'<strong>Probability to Bucket: </strong>' + (exp.pc * 100) + '% :: ' +
'<strong>Actual Probability of User to Bucket: </strong>' + (exp.p * 100) + '% ';
return this.buildDivRow(html);
},
getAllRunningExperiments: function() {
var all_exps = [];
var curr_exp;
var html = '';
for (var i = 0; i < ALL_EXP.length; i++) {
curr_exp = ALL_EXP[i];
all_exps.push(curr_exp.id);
}
html += '<strong>All Running Experiments (by ID): </strong>' + all_exps.join(', ');
return this.buildDivRow(html);
},
getActiveExperiments: function() {
var active_exps = [];
var curr_exp;
var html = '';
for (var i = 0; i < CURR_EXP.length; i++) {
curr_exp = CURR_EXP[i];
active_exps.push(curr_exp.e);
}
html += '<strong>All ACTIVE Experiments (by ID): </strong>' + active_exps.join(', ');
return this.buildDivRow(html);
},
getActiveExperimentsFullInfo: function() {
var curr_exp;
var wrap = document.createElement('div');
for (var i = 0; i < CURR_EXP.length; i++) {
curr_exp = CURR_EXP[i];
wrap.appendChild(this.getExperimentFullInfo(curr_exp));
}
return wrap;
},
loopAndBuild: function(exps, buildFn) {
var wrap = document.createElement('div');
for (var i = 0; i < exps.length; i++) {
wrap.appendChild(buildFn.call(this, exps[i]));
}
return wrap;
},
createAndAddStylesheet: function(styles) {
var css = document.createElement('style');
css.type = 'text/css';
css.id = "qubit-debugger-styles";
if (css.styleSheet) {
css.styleSheet.cssText = styles;
} else {
css.appendChild(document.createTextNode(styles));
}
HEAD.appendChild(css);
},
buildModal: function() {
var html = "";
html += "<div id=\"qubit-modal-overlay\"> ";
html += " <div id=\"qubit-modal-container\">";
html += " <div id=\"qubit-modal-close-x\"><\/div>";
html += " <div id=\"qubit-header\">Qubit Debugger \/ Experiment Info<\/div>";
html += " <div id=\"qubit-options\">";
html += " <h3>Experiments & Variations Mapping<\/h3>";
html += " <input type=\"checkbox\" id=\"qubit-all-running-experiments\" \/>";
html += " <label for=\"qubit-all-running-experiments\"><span><\/span>All Running Experiments*<\/label>";
html += " <input type=\"checkbox\" id=\"qubit-active-experiments\" \/>";
html += " <label for=\"qubit-active-experiments\"><span><\/span>Active Experiments<\/label>";
html += " <br \/>";
html += " <input type=\"checkbox\" id=\"qubit-variations-index-map\" \/>";
html += " <label for=\"qubit-variations-index-map\"><span><\/span>Variations Mapping By Index (0 is Original)<\/label>";
html += " <br \/>";
html += " <input type=\"checkbox\" id=\"qubit-variations-id-map\" \/>";
html += " <label for=\"qubit-variations-id-map\"><span><\/span>Variations Mapping By ID<\/label>";
html += " <br \/>";
html += " <input type=\"checkbox\" id=\"qubit-variations-name-ckg\" \/>";
html += " <label for=\"qubit-variations-name-ckg\"><span><\/span>Variations Mapping By Creative Key Group<\/label>";
html += " <br \/>";
html += " <input type=\"checkbox\" id=\"qubit-smartserve-id\" \/>";
html += " <label for=\"qubit-smartserve-id\"><span><\/span>SmartServe Script ID<\/label>";
html += " <br \/>";
html += " <input type=\"checkbox\" id=\"qubit-active-experiments-full-info\" \/>";
html += " <label for=\"qubit-active-experiments-full-info\"><span><\/span>All Active Experiments Full Info<\/label>";
html += " <br \/>";
html += " <p class=\"note\">* All \"running experiments\" refers to all currently active layers (i.e. experiments) within a container, <strong>regardless of bucketing\/segmentation criteria<\/strong><\/p>";
html += " <p class=\"note\">* \"Active\" experiments refers to all layers (i.e. experiments) within a container <strong>that you are currently bucketed into<\/strong><\/p>";
html += " <\/div>";
html += " <div class=\"center-me\"><button id=\"qubit-go\">Go!<\/button><\/div>";
html += " <div id=\"qubit-results\"><\/div>";
html += " <div id=\"qubit-help\">";
html += " <a id=\"qubit-modal-destroy\" href=\"#\">Start Over<\/a>";
html += " <a href=\"http:\/\/docs.qubitproducts.com\" target=\"_blank\">Qubit Docs<\/a>";
html += " <\/div>";
html += " <div id=\"qubit-close-modal-wrap\" class=\"center-me\"><button id=\"qubit-close-modal\">CLOSE<\/button><\/div>";
html += " <\/div>";
html += "<\/div>";
this.el.innerHTML = html;
BODY.appendChild(this.el);
},
destroy: function() {
this.el.remove();
var stylesheet = document.getElementById('qubit-debugger-styles');
if (stylesheet) stylesheet.remove();
},
showResults: function() {
document.getElementById('qubit-options').style.display = "none";
document.getElementById('qubit-go').style.display = "none";
document.getElementById('qubit-results').style.display = "block";
document.getElementById('qubit-help').style.display = "block";
document.getElementById('qubit-close-modal-wrap').style.display = "block";
},
handleSelectedOptions: function() {
var results = document.getElementById('qubit-results');
var self = this;
if (document.getElementById('qubit-all-running-experiments').checked) {
results.appendChild(this.getAllRunningExperiments());
}
if (document.getElementById('qubit-active-experiments').checked) {
results.appendChild(this.getActiveExperiments());
}
if (document.getElementById('qubit-variations-index-map').checked) {
results.appendChild(this.loopAndBuild(CURR_EXP, this.getExperimentVariationMapByIndex));
}
if (document.getElementById('qubit-variations-id-map').checked) {
results.appendChild(this.loopAndBuild(CURR_EXP, this.getExperimentVariationMapById));
}
if (document.getElementById('qubit-variations-name-ckg').checked) {
results.appendChild(this.loopAndBuild(CURR_EXP, this.getMultivariateCreativeKeyGroup));
}
if (document.getElementById('qubit-smartserve-id').checked) {
results.appendChild(this.getSmartServeScriptId());
}
if (document.getElementById('qubit-active-experiments-full-info').checked) {
results.appendChild(this.getActiveExperimentsFullInfo());
}
},
setGoBtnHandler: function() {
var self = this;
var go_btn = document.getElementById('qubit-go');
go_btn.addEventListener('click', function() {
self.handleSelectedOptions();
self.showResults();
});
},
setCloseBtnsHandler: function() {
var self = this;
var close_btn = document.getElementById('qubit-close-modal');
var x_btn = document.getElementById('qubit-modal-close-x');
close_btn.addEventListener('click', function() {
self.destroy();
});
x_btn.addEventListener('click', function() {
self.destroy();
});
},
setStartOverBtnHandler: function() {
var self = this;
var start_over_btn = document.getElementById('qubit-modal-destroy');
start_over_btn.addEventListener('click', function(e) {
e.preventDefault();
self.destroy();
self.init();
});
},
setListeners: function() {
this.setGoBtnHandler();
this.setCloseBtnsHandler();
this.setStartOverBtnHandler();
},
init: function() {
if (this.checkQubitVarsLoaded()) {
this.createAndAddStylesheet(this.styles);
this.buildModal();
this.setListeners();
}
}
};
var modal = new Modal();
modal.init();
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment