@cascayd/experiment 0.3.0 â 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/shopify-handler-browser.js +134 -24
- package/package.json +1 -1
|
@@ -18,10 +18,16 @@
|
|
|
18
18
|
(function() {
|
|
19
19
|
'use strict';
|
|
20
20
|
|
|
21
|
+
console.log('[cascayd-shopify] đ Handler script starting...');
|
|
22
|
+
console.log('[cascayd-shopify] Document readyState:', document.readyState);
|
|
23
|
+
console.log('[cascayd-shopify] Window object available:', typeof window !== 'undefined');
|
|
24
|
+
|
|
21
25
|
// Cookie helper functions (matching SDK's cookie logic)
|
|
22
26
|
function getCookie(name) {
|
|
23
27
|
const match = document.cookie.match(new RegExp('(?:^|; )' + name.replace(/[.$?*|{}()\[\]\\\/\+^]/g, '\\$&') + '=([^;]*)'));
|
|
24
|
-
|
|
28
|
+
const value = match ? decodeURIComponent(match[1]) : null;
|
|
29
|
+
console.log('[cascayd-shopify] đĻ getCookie:', name, '=', value);
|
|
30
|
+
return value;
|
|
25
31
|
}
|
|
26
32
|
|
|
27
33
|
function setCookie(name, value) {
|
|
@@ -33,11 +39,15 @@
|
|
|
33
39
|
}
|
|
34
40
|
|
|
35
41
|
function readVariantChoice(experimentId) {
|
|
36
|
-
|
|
42
|
+
const choice = getCookie(getVariantCookieKey(experimentId));
|
|
43
|
+
console.log('[cascayd-shopify] đ Read variant choice:', { experimentId, choice });
|
|
44
|
+
return choice;
|
|
37
45
|
}
|
|
38
46
|
|
|
39
47
|
function persistVariantChoice(experimentId, variantId) {
|
|
40
|
-
|
|
48
|
+
const key = getVariantCookieKey(experimentId);
|
|
49
|
+
console.log('[cascayd-shopify] đž Persisting variant choice:', { experimentId, variantId, cookieKey: key });
|
|
50
|
+
setCookie(key, variantId);
|
|
41
51
|
}
|
|
42
52
|
|
|
43
53
|
// API configuration (same as SDK)
|
|
@@ -54,16 +64,22 @@
|
|
|
54
64
|
|
|
55
65
|
// Direct API call functions (same logic as SDK)
|
|
56
66
|
async function getVariantStatus(experimentId, variantId) {
|
|
57
|
-
const
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
67
|
+
const url = BASE_URL + '/experiments/' + encodeURIComponent(experimentId) + '/variants/' + encodeURIComponent(variantId) + '/status';
|
|
68
|
+
console.log('[cascayd-shopify] đ Fetching variant status:', { experimentId, variantId, url });
|
|
69
|
+
try {
|
|
70
|
+
const res = await fetch(url);
|
|
71
|
+
const data = await res.json();
|
|
72
|
+
console.log('[cascayd-shopify] â
Variant status response:', { status: res.status, data });
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const err = new Error('Variant status failed: ' + res.status);
|
|
75
|
+
err.payload = data;
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
return data;
|
|
79
|
+
} catch (error) {
|
|
80
|
+
console.error('[cascayd-shopify] â getVariantStatus error:', error);
|
|
81
|
+
throw error;
|
|
65
82
|
}
|
|
66
|
-
return data;
|
|
67
83
|
}
|
|
68
84
|
|
|
69
85
|
function chooseByWeight(variants) {
|
|
@@ -91,26 +107,33 @@
|
|
|
91
107
|
|
|
92
108
|
// Assign variant using same logic as SDK
|
|
93
109
|
async function assignVariantDirect(experimentId) {
|
|
110
|
+
console.log('[cascayd-shopify] đ˛ assignVariantDirect called for:', experimentId);
|
|
94
111
|
const baseStatus = await getVariantStatus(experimentId, 'control');
|
|
112
|
+
console.log('[cascayd-shopify] đ Base status received:', baseStatus);
|
|
95
113
|
const weights = baseStatus.weights || {};
|
|
96
114
|
const variants = Object.entries(weights).map(function(entry) {
|
|
97
115
|
return { id: entry[0], weight: entry[1] };
|
|
98
116
|
});
|
|
117
|
+
console.log('[cascayd-shopify] đ Variants from weights:', variants);
|
|
99
118
|
|
|
100
119
|
const allVariants = variants.length > 0 ? variants : baseStatusToVariants(baseStatus);
|
|
120
|
+
console.log('[cascayd-shopify] đ¯ All variants for selection:', allVariants);
|
|
101
121
|
let candidate = chooseByWeight(allVariants);
|
|
122
|
+
console.log('[cascayd-shopify] đ˛ Selected candidate:', candidate);
|
|
102
123
|
let finalStatus = null;
|
|
103
124
|
|
|
104
125
|
if (candidate) {
|
|
105
126
|
try {
|
|
106
127
|
finalStatus = await getVariantStatus(experimentId, candidate);
|
|
128
|
+
console.log('[cascayd-shopify] â
Final status for candidate:', finalStatus);
|
|
107
129
|
} catch (error) {
|
|
108
|
-
console.error('[cascayd-shopify] getVariantStatus failed', { experimentId: experimentId, candidate: candidate, error: error });
|
|
130
|
+
console.error('[cascayd-shopify] â getVariantStatus failed for candidate', { experimentId: experimentId, candidate: candidate, error: error });
|
|
109
131
|
// Fall back to candidate
|
|
110
132
|
}
|
|
111
133
|
}
|
|
112
134
|
|
|
113
135
|
const serving = (finalStatus && finalStatus.serving_variant_id) || candidate || 'control';
|
|
136
|
+
console.log('[cascayd-shopify] â
Final serving variant:', serving);
|
|
114
137
|
return { variantId: serving };
|
|
115
138
|
}
|
|
116
139
|
|
|
@@ -171,33 +194,53 @@
|
|
|
171
194
|
async function waitForSDK(maxAttempts, interval) {
|
|
172
195
|
maxAttempts = maxAttempts || 10;
|
|
173
196
|
interval = interval || 100;
|
|
197
|
+
console.log('[cascayd-shopify] âŗ Waiting for SDK...', { maxAttempts, interval });
|
|
174
198
|
|
|
175
199
|
return new Promise(function(resolve) {
|
|
176
200
|
let attempts = 0;
|
|
177
201
|
const checkSDK = function() {
|
|
178
202
|
attempts++;
|
|
203
|
+
console.log('[cascayd-shopify] đ Checking for SDK (attempt ' + attempts + '/' + maxAttempts + ')');
|
|
204
|
+
|
|
179
205
|
// Check if SDK functions are available on window
|
|
180
206
|
let assignVariantFn = null;
|
|
181
207
|
let recordFn = null;
|
|
182
208
|
|
|
183
209
|
if (typeof window !== 'undefined') {
|
|
184
|
-
|
|
185
|
-
|
|
210
|
+
console.log('[cascayd-shopify] đĒ Window available, checking functions...');
|
|
211
|
+
console.log('[cascayd-shopify] - window.assignVariant:', typeof window.assignVariant);
|
|
212
|
+
console.log('[cascayd-shopify] - window.record:', typeof window.record);
|
|
213
|
+
console.log('[cascayd-shopify] - window.Cascayd:', typeof window.Cascayd);
|
|
214
|
+
console.log('[cascayd-shopify] - window.initCascayd:', typeof window.initCascayd);
|
|
215
|
+
|
|
216
|
+
if (window.assignVariant) {
|
|
217
|
+
assignVariantFn = window.assignVariant;
|
|
218
|
+
console.log('[cascayd-shopify] â
Found window.assignVariant');
|
|
219
|
+
}
|
|
220
|
+
if (window.record) {
|
|
221
|
+
recordFn = window.record;
|
|
222
|
+
console.log('[cascayd-shopify] â
Found window.record');
|
|
223
|
+
}
|
|
186
224
|
// Try Cascayd namespace
|
|
187
225
|
if (!assignVariantFn && window.Cascayd && window.Cascayd.assignVariant) {
|
|
188
226
|
assignVariantFn = window.Cascayd.assignVariant;
|
|
227
|
+
console.log('[cascayd-shopify] â
Found Cascayd.assignVariant');
|
|
189
228
|
}
|
|
190
229
|
if (!recordFn && window.Cascayd && window.Cascayd.record) {
|
|
191
230
|
recordFn = window.Cascayd.record;
|
|
231
|
+
console.log('[cascayd-shopify] â
Found Cascayd.record');
|
|
192
232
|
}
|
|
193
233
|
}
|
|
194
234
|
|
|
195
235
|
if (assignVariantFn && recordFn) {
|
|
236
|
+
console.log('[cascayd-shopify] â
SDK functions found! Using SDK.');
|
|
196
237
|
resolve({ assignVariant: assignVariantFn, record: recordFn });
|
|
197
238
|
} else if (attempts >= maxAttempts) {
|
|
239
|
+
console.log('[cascayd-shopify] â ī¸ SDK not found after ' + maxAttempts + ' attempts. Using direct API calls.');
|
|
198
240
|
// Use direct API calls as fallback
|
|
199
241
|
resolve({ assignVariant: assignVariantDirect, record: recordDirect });
|
|
200
242
|
} else {
|
|
243
|
+
console.log('[cascayd-shopify] âŗ SDK not ready, retrying in ' + interval + 'ms...');
|
|
201
244
|
setTimeout(checkSDK, interval);
|
|
202
245
|
}
|
|
203
246
|
};
|
|
@@ -207,8 +250,9 @@
|
|
|
207
250
|
|
|
208
251
|
// Record impression event
|
|
209
252
|
async function recordImpression(experimentId, variantId, sdk) {
|
|
253
|
+
console.log('[cascayd-shopify] đ Recording impression:', { experimentId, variantId, hasSDK: !!sdk, hasRecordFn: !!(sdk && sdk.record) });
|
|
210
254
|
if (!sdk || !sdk.record) {
|
|
211
|
-
console.
|
|
255
|
+
console.warn('[cascayd-shopify] â ī¸ Record function not available, skipping impression tracking');
|
|
212
256
|
return;
|
|
213
257
|
}
|
|
214
258
|
|
|
@@ -217,25 +261,32 @@
|
|
|
217
261
|
experimentId: experimentId,
|
|
218
262
|
variantId: variantId
|
|
219
263
|
});
|
|
264
|
+
console.log('[cascayd-shopify] â
Impression recorded successfully');
|
|
220
265
|
} catch (error) {
|
|
221
|
-
console.error('[cascayd-shopify] Failed to record impression', error);
|
|
266
|
+
console.error('[cascayd-shopify] â Failed to record impression:', error);
|
|
222
267
|
}
|
|
223
268
|
}
|
|
224
269
|
|
|
225
270
|
// Process a single experiment
|
|
226
271
|
async function processExperiment(experimentId, variantElements, sdk) {
|
|
272
|
+
console.log('[cascayd-shopify] đŦ Processing experiment:', { experimentId, variantElementCount: variantElements.length, hasSDK: !!sdk });
|
|
273
|
+
|
|
227
274
|
// Check if we already have a variant choice stored
|
|
228
275
|
let variantId = readVariantChoice(experimentId);
|
|
276
|
+
console.log('[cascayd-shopify] đ Variant choice from cookie:', variantId);
|
|
229
277
|
|
|
230
278
|
// If no stored choice, assign a variant
|
|
231
279
|
if (!variantId) {
|
|
280
|
+
console.log('[cascayd-shopify] đ No stored variant, assigning new one...');
|
|
232
281
|
if (sdk && sdk.assignVariant) {
|
|
233
282
|
try {
|
|
283
|
+
console.log('[cascayd-shopify] đ˛ Calling SDK assignVariant...');
|
|
234
284
|
const result = await sdk.assignVariant(experimentId);
|
|
235
285
|
variantId = result.variantId;
|
|
286
|
+
console.log('[cascayd-shopify] â
SDK assigned variant:', variantId);
|
|
236
287
|
persistVariantChoice(experimentId, variantId);
|
|
237
288
|
} catch (error) {
|
|
238
|
-
console.error('[cascayd-shopify] Failed to assign variant', error);
|
|
289
|
+
console.error('[cascayd-shopify] â Failed to assign variant via SDK:', error);
|
|
239
290
|
// Fallback: use first available variant or control
|
|
240
291
|
const variants = [];
|
|
241
292
|
variantElements.forEach(function(el) {
|
|
@@ -245,51 +296,88 @@
|
|
|
245
296
|
}
|
|
246
297
|
});
|
|
247
298
|
variantId = variants.indexOf('control') >= 0 ? 'control' : (variants[0] || 'control');
|
|
299
|
+
console.log('[cascayd-shopify] đ Using fallback variant:', variantId, 'from available:', variants);
|
|
248
300
|
persistVariantChoice(experimentId, variantId);
|
|
249
301
|
}
|
|
250
302
|
} else {
|
|
251
|
-
console.error('[cascayd-shopify] No assignVariant function available');
|
|
303
|
+
console.error('[cascayd-shopify] â No assignVariant function available');
|
|
252
304
|
// This shouldn't happen since waitForSDK always returns functions (direct API or SDK)
|
|
253
305
|
}
|
|
306
|
+
} else {
|
|
307
|
+
console.log('[cascayd-shopify] â
Using existing variant choice:', variantId);
|
|
254
308
|
}
|
|
255
309
|
|
|
256
310
|
// Show the selected variant, hide all others
|
|
311
|
+
console.log('[cascayd-shopify] đī¸ Showing/hiding variants. Selected:', variantId);
|
|
257
312
|
let hasRecorded = false;
|
|
258
|
-
variantElements.forEach(function(el) {
|
|
313
|
+
variantElements.forEach(function(el, index) {
|
|
259
314
|
const elVariantId = el.getAttribute('data-cascayd-variant');
|
|
315
|
+
console.log('[cascayd-shopify] Element ' + (index + 1) + ':', { variantId: elVariantId, matches: elVariantId === variantId });
|
|
316
|
+
|
|
260
317
|
if (elVariantId === variantId) {
|
|
318
|
+
console.log('[cascayd-shopify] â
Showing element with variant:', elVariantId);
|
|
319
|
+
// Remove CSS hiding and mark as visible
|
|
320
|
+
el.classList.remove('cascayd-hidden');
|
|
321
|
+
el.classList.add('cascayd-visible');
|
|
322
|
+
// Detect if element should be inline or block based on original display
|
|
323
|
+
const computedStyle = window.getComputedStyle(el);
|
|
324
|
+
if (computedStyle.display === 'inline' || computedStyle.display === 'inline-block') {
|
|
325
|
+
el.classList.add('cascayd-visible-inline');
|
|
326
|
+
}
|
|
261
327
|
el.style.display = '';
|
|
262
328
|
el.removeAttribute('hidden');
|
|
329
|
+
console.log('[cascayd-shopify] â
Element shown, classes:', el.className);
|
|
263
330
|
// Record impression only once for the shown variant
|
|
264
331
|
if (!hasRecorded) {
|
|
265
332
|
recordImpression(experimentId, variantId, sdk);
|
|
266
333
|
hasRecorded = true;
|
|
267
334
|
}
|
|
268
335
|
} else {
|
|
336
|
+
console.log('[cascayd-shopify] đĢ Hiding element with variant:', elVariantId);
|
|
269
337
|
el.style.display = 'none';
|
|
270
338
|
el.setAttribute('hidden', 'hidden');
|
|
339
|
+
el.classList.add('cascayd-hidden');
|
|
340
|
+
el.classList.remove('cascayd-visible', 'cascayd-visible-inline');
|
|
271
341
|
}
|
|
272
342
|
});
|
|
343
|
+
console.log('[cascayd-shopify] â
Finished processing experiment:', experimentId);
|
|
273
344
|
}
|
|
274
345
|
|
|
275
346
|
// Main initialization function
|
|
276
347
|
async function initCascaydExperiments() {
|
|
348
|
+
console.log('[cascayd-shopify] đ¯ initCascaydExperiments called');
|
|
349
|
+
console.log('[cascayd-shopify] đ Document readyState:', document.readyState);
|
|
350
|
+
|
|
277
351
|
// Wait for SDK to be loaded (with timeout)
|
|
352
|
+
console.log('[cascayd-shopify] âŗ Waiting for SDK...');
|
|
278
353
|
const sdk = await waitForSDK(50, 100);
|
|
354
|
+
console.log('[cascayd-shopify] â
SDK ready:', { hasSDK: !!sdk, hasAssignVariant: !!(sdk && sdk.assignVariant), hasRecord: !!(sdk && sdk.record) });
|
|
279
355
|
|
|
280
356
|
// Find all elements with data-cascayd-experiment attribute
|
|
281
357
|
const allExperimentElements = document.querySelectorAll('[data-cascayd-experiment]');
|
|
358
|
+
console.log('[cascayd-shopify] đ Found experiment elements:', allExperimentElements.length);
|
|
282
359
|
|
|
283
360
|
if (allExperimentElements.length === 0) {
|
|
361
|
+
console.log('[cascayd-shopify] âšī¸ No experiment elements found, exiting');
|
|
284
362
|
return; // No experiments to process
|
|
285
363
|
}
|
|
286
364
|
|
|
365
|
+
// Log all found elements
|
|
366
|
+
allExperimentElements.forEach(function(el, index) {
|
|
367
|
+
const expId = el.getAttribute('data-cascayd-experiment');
|
|
368
|
+
const varId = el.getAttribute('data-cascayd-variant');
|
|
369
|
+
console.log('[cascayd-shopify] Element ' + (index + 1) + ':', { experimentId: expId, variantId: varId, tagName: el.tagName });
|
|
370
|
+
});
|
|
371
|
+
|
|
287
372
|
// Group elements by experiment ID
|
|
288
373
|
const experiments = new Map();
|
|
289
374
|
|
|
290
375
|
allExperimentElements.forEach(function(el) {
|
|
291
376
|
const experimentId = el.getAttribute('data-cascayd-experiment');
|
|
292
|
-
if (!experimentId)
|
|
377
|
+
if (!experimentId) {
|
|
378
|
+
console.warn('[cascayd-shopify] â ī¸ Element missing experiment ID:', el);
|
|
379
|
+
return;
|
|
380
|
+
}
|
|
293
381
|
|
|
294
382
|
if (!experiments.has(experimentId)) {
|
|
295
383
|
experiments.set(experimentId, []);
|
|
@@ -297,27 +385,49 @@
|
|
|
297
385
|
experiments.get(experimentId).push(el);
|
|
298
386
|
});
|
|
299
387
|
|
|
388
|
+
console.log('[cascayd-shopify] đ Grouped experiments:', Array.from(experiments.keys()).map(function(id) {
|
|
389
|
+
return { experimentId: id, variantCount: experiments.get(id).length };
|
|
390
|
+
}));
|
|
391
|
+
|
|
300
392
|
// Process each experiment
|
|
301
393
|
const promises = [];
|
|
302
394
|
experiments.forEach(function(variantElements, experimentId) {
|
|
395
|
+
console.log('[cascayd-shopify] đ Processing experiment:', experimentId, 'with', variantElements.length, 'variants');
|
|
303
396
|
promises.push(processExperiment(experimentId, variantElements, sdk));
|
|
304
397
|
});
|
|
305
398
|
|
|
399
|
+
console.log('[cascayd-shopify] âŗ Waiting for all experiments to process...');
|
|
306
400
|
await Promise.all(promises);
|
|
401
|
+
console.log('[cascayd-shopify] â
All experiments processed!');
|
|
307
402
|
}
|
|
308
403
|
|
|
309
404
|
// Initialize when DOM is ready
|
|
405
|
+
console.log('[cascayd-shopify] đ Setting up initialization...');
|
|
310
406
|
if (document.readyState === 'loading') {
|
|
311
|
-
|
|
407
|
+
console.log('[cascayd-shopify] âŗ DOM still loading, waiting for DOMContentLoaded...');
|
|
408
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
409
|
+
console.log('[cascayd-shopify] â
DOMContentLoaded fired!');
|
|
410
|
+
initCascaydExperiments();
|
|
411
|
+
});
|
|
312
412
|
} else {
|
|
313
413
|
// DOM already ready
|
|
414
|
+
console.log('[cascayd-shopify] â
DOM already ready, initializing immediately');
|
|
314
415
|
initCascaydExperiments();
|
|
315
416
|
}
|
|
316
417
|
|
|
317
418
|
// Handle Shopify's dynamic content (sections loaded via AJAX)
|
|
318
419
|
if (typeof window.addEventListener !== 'undefined') {
|
|
319
|
-
|
|
320
|
-
document.addEventListener('shopify:section:
|
|
420
|
+
console.log('[cascayd-shopify] đ Setting up Shopify section event listeners...');
|
|
421
|
+
document.addEventListener('shopify:section:load', function() {
|
|
422
|
+
console.log('[cascayd-shopify] đ shopify:section:load event fired, re-initializing...');
|
|
423
|
+
initCascaydExperiments();
|
|
424
|
+
});
|
|
425
|
+
document.addEventListener('shopify:section:reorder', function() {
|
|
426
|
+
console.log('[cascayd-shopify] đ shopify:section:reorder event fired, re-initializing...');
|
|
427
|
+
initCascaydExperiments();
|
|
428
|
+
});
|
|
321
429
|
}
|
|
430
|
+
|
|
431
|
+
console.log('[cascayd-shopify] â
Handler script initialization complete');
|
|
322
432
|
})();
|
|
323
433
|
|