Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save meganmcchesney/81616d260cb21ab055f4402efded9955 to your computer and use it in GitHub Desktop.

Select an option

Save meganmcchesney/81616d260cb21ab055f4402efded9955 to your computer and use it in GitHub Desktop.
ADI - Web Experimentation script
@smillin-opti
Copy link

// Prepare global variables
window.personas = window.personas || [];
window.roles = window.roles || [];

// Attach navigation listener
window.navigation.addEventListener("navigate", (event) => {
console.group("Optimizely Configured Commerce: Navigation");
console.log("Activating for URL: ", window.location.pathname + window.location.search + window.location.hash);
window.optimizely.push({ type: "activate" });
console.groupEnd();
});

// Fetch session, cart, and branch data, then update Optimizely with combined attributes
function updateOptimizelyAttributes() {
Promise.all([
fetch('/api/v1/sessions/current').then(r => r.json()),
fetch('/api/v1/carts/current').then(r => r.json()),
fetch('/api/v1/ShopMyBranch').then(r => r.json())
]).then(([sessionData, cartData, branchData]) => {
window.optimizely = window.optimizely || [];

    console.group("Optimizely Configured Commerce: Processing session, cart, and branch data");
    console.log("Session Data:", sessionData);
    console.log("Cart Data:", cartData);
    console.log("Branch Data:", branchData);

    let attributes = {
        cc_defaultSegment: sessionData.persona,
        cc_segments: sessionData.personas.map(p => p.name).join(", "),
        cc_currency: sessionData.currency.currencyCode,
        cc_language: sessionData.language.languageCode,
        cc_deviceType: sessionData.deviceType,
        cc_fulfillmentMethod: sessionData.fulfillmentMethod,
        cc_userRoles: sessionData.userRoles
    };

    if (sessionData.billTo) {
        attributes.cc_customerCity = sessionData.billTo.city;
        attributes.cc_customerState = sessionData.billTo.state.abbreviation;
        attributes.cc_customerCountry = sessionData.billTo.country.abbreviation;
    } else {
        attributes.cc_customerCity = null;
        attributes.cc_customerState = null;
        attributes.cc_customerCountry = null;
    }

    let sfCategoryValue = null;
    if (cartData.billTo && cartData.billTo.properties) {
        sfCategoryValue = findPropertyCaseInsensitive(cartData.billTo.properties, "sf_opcategory");
    }
    if (!sfCategoryValue && cartData.shipTo && cartData.shipTo.properties) {
        sfCategoryValue = findPropertyCaseInsensitive(cartData.shipTo.properties, "sf_opcategory");
    }

    if (sfCategoryValue) {
        attributes.cc_category = sfCategoryValue;
    }

    if (branchData && branchData.name) {
        attributes.cc_branchName = branchData.name;
    }

    window.personas = sessionData.personas.map(p => p.name);
    window.roles = sessionData.userRoles ? sessionData.userRoles.split(",").map(r => r.trim()) : [];

    console.log("Category Value:", sfCategoryValue);
    console.log("Branch Name:", branchData?.name);
    console.log("Updating processed values for JS Conditions (personas, roles)", window.personas, window.roles);
    console.log("Updating visitor attributes", attributes);

    window.optimizely.push({ type: "user", attributes });

    console.log("Invoking `activate` to apply changes based upon incoming attributes");
    window.optimizely.push({ type: "activate" });
    console.groupEnd();
}).catch(error => {
    console.error("Error fetching session or cart data:", error);
});

}
updateOptimizelyAttributes();

function findPropertyCaseInsensitive(obj, targetKey) {
if (!obj) return null;
const keys = Object.keys(obj);
const foundKey = keys.find(key => key.toLowerCase() === targetKey.toLowerCase());
return foundKey ? obj[foundKey] : null;
}

//New code starting 092525 for Scroll Event

// --- Optimizely Web: Scroll Depth Tracking (gated) ---
// Initializes only if an allow-listed experiment is active OR a variation sets a flag.
// Sends scroll_25 / scroll_50 / scroll_75 / scroll_100 once per pageview.

(function () {
if (window.__optlyScrollBootstrapInstalled) return;
window.__optlyScrollBootstrapInstalled = true;

// ======== CONFIG ========
// Add experiment KEYS here when you want scroll tracking enabled for them.
// (You can find an experiment "Key" in the Optimizely UI on the experiment's Settings page.)
var EXP_ALLOWLIST = [
    // 'my_scroll_test_v1',
    // 'plp_personalization_v3'
];

// ======== UTIL: Optimizely ready ========
function onOptReady(cb, tries) {
    tries = tries || 0;
    if (window.optimizely && window.optimizely.get && window.optimizely.get("state")) {
        try { cb(); } catch (e) { }
        return;
    }
    if (tries > 120) return; // ~6s cap
    setTimeout(function () { onOptReady(cb, tries + 1); }, 50);
}

// ======== Gate logic ========
function getActiveExperimentKeys() {
    try {
        var state = window.optimizely.get("state");
        var data = window.optimizely.get("data");
        var activeIds = state.getActiveExperimentIds ? state.getActiveExperimentIds() : [];
        if (!activeIds || !activeIds.length) return [];
        // build id -> key map once
        if (!window.__optlyIdToKey) {
            window.__optlyIdToKey = {};
            (data.experiments || []).forEach(function (exp) {
                window.__optlyIdToKey[exp.id] = exp.key; // key is human-friendly identifier
            });
        }
        return activeIds.map(function (id) { return window.__optlyIdToKey[id]; }).filter(Boolean);
    } catch (e) { return []; }
}

function intersects(a, b) {
    var setB = new Set(b);
    for (var i = 0; i < a.length; i++) if (setB.has(a[i])) return true;
    return false;
}

function shouldEnable() {
    // Variation-level opt-in flag (set in variation code if you prefer)
    if (window.__enableScrollTracking === true) return true;

    // Allow-list by active experiment keys
    if (EXP_ALLOWLIST && EXP_ALLOWLIST.length) {
        var activeKeys = getActiveExperimentKeys();
        if (activeKeys.length && intersects(activeKeys, EXP_ALLOWLIST)) return true;
    }
    return false;
}

// ======== Scroll Tracker (same as before, only installs when gated) ========
function installScrollTracker() {
    if (window.__optlyScrollTrackerInstalled) return;
    window.__optlyScrollTrackerInstalled = true;

    var THRESHOLDS = [25, 50, 75, 100];
    var fired = new Set();

    function sendEvent(evtName) {
        window.optimizely = window.optimizely || [];
        window.optimizely.push({ type: "event", eventName: evtName });
    }

    function getScrollPercent() {
        var doc = document.documentElement;
        var body = document.body;
        var scrollTop = window.pageYOffset || doc.scrollTop || body.scrollTop || 0;
        var viewport = window.innerHeight || doc.clientHeight || 0;
        var fullHeight = Math.max(
            body.scrollHeight, body.offsetHeight,
            doc.clientHeight, doc.scrollHeight, doc.offsetHeight
        );
        var percent = ((scrollTop + viewport) / fullHeight) * 100;
        return Math.min(100, Math.max(0, Math.round(percent)));
    }

    function checkThresholds() {
        var pct = getScrollPercent();
        for (var i = 0; i < THRESHOLDS.length; i++) {
            var t = THRESHOLDS[i];
            if (!fired.has(t) && pct >= t) {
                fired.add(t);
                sendEvent("scroll_" + t);
            }
        }
    }

    var ticking = false;
    function onScroll() {
        if (!ticking) {
            window.requestAnimationFrame(function () {
                checkThresholds();
                ticking = false;
            });
            ticking = true;
        }
    }

    window.addEventListener("scroll", onScroll, { passive: true });
    window.addEventListener("resize", onScroll);
    window.addEventListener("load", checkThresholds);
    setTimeout(checkThresholds, 1500); // late content/SPAs
}

// ======== Bootstrap when Optimizely is ready ========
function maybeInit() {
    if (shouldEnable()) installScrollTracker();
}

onOptReady(function () {
    // Run once at load
    maybeInit();

    // Also react to decisions changing (e.g., SPA nav or late bucketing)
    try {
        window.optimizely.push({
            type: "addListener",
            filter: { type: "lifecycle", name: "campaignDecided" }, // fires on decision events
            handler: function () { maybeInit(); }
        });
    } catch (e) {
        // As a fallback, poll a few times
        var tries = 0;
        var iv = setInterval(function () {
            tries++;
            maybeInit();
            if (tries > 60 || window.__optlyScrollTrackerInstalled) clearInterval(iv);
        }, 250);
    }
});

})();

``

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment