@searchspring/snap-tracker 0.20.0
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/LICENSE +21 -0
- package/README.md +474 -0
- package/dist/cjs/BeaconEvent.d.ts +12 -0
- package/dist/cjs/BeaconEvent.d.ts.map +1 -0
- package/dist/cjs/BeaconEvent.js +22 -0
- package/dist/cjs/PixelEvent.d.ts +9 -0
- package/dist/cjs/PixelEvent.d.ts.map +1 -0
- package/dist/cjs/PixelEvent.js +63 -0
- package/dist/cjs/TrackEvent.d.ts +11 -0
- package/dist/cjs/TrackEvent.d.ts.map +1 -0
- package/dist/cjs/TrackEvent.js +31 -0
- package/dist/cjs/Tracker.d.ts +31 -0
- package/dist/cjs/Tracker.d.ts.map +1 -0
- package/dist/cjs/Tracker.js +491 -0
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -0
- package/dist/cjs/index.js +16 -0
- package/dist/cjs/types.d.ts +144 -0
- package/dist/cjs/types.d.ts.map +1 -0
- package/dist/cjs/types.js +30 -0
- package/dist/esm/BeaconEvent.d.ts +12 -0
- package/dist/esm/BeaconEvent.d.ts.map +1 -0
- package/dist/esm/BeaconEvent.js +16 -0
- package/dist/esm/PixelEvent.d.ts +9 -0
- package/dist/esm/PixelEvent.d.ts.map +1 -0
- package/dist/esm/PixelEvent.js +57 -0
- package/dist/esm/TrackEvent.d.ts +11 -0
- package/dist/esm/TrackEvent.d.ts.map +1 -0
- package/dist/esm/TrackEvent.js +25 -0
- package/dist/esm/Tracker.d.ts +31 -0
- package/dist/esm/Tracker.d.ts.map +1 -0
- package/dist/esm/Tracker.js +462 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +2 -0
- package/dist/esm/types.d.ts +144 -0
- package/dist/esm/types.d.ts.map +1 -0
- package/dist/esm/types.js +27 -0
- package/package.json +33 -0
|
@@ -0,0 +1,462 @@
|
|
|
1
|
+
import deepmerge from 'deepmerge';
|
|
2
|
+
import { v4 as uuidv4 } from 'uuid';
|
|
3
|
+
import { StorageStore, StorageType } from '@searchspring/snap-store-mobx';
|
|
4
|
+
import { cookies, featureFlags, version, DomTargeter, getContext } from '@searchspring/snap-toolbox';
|
|
5
|
+
import { TrackEvent } from './TrackEvent';
|
|
6
|
+
import { PixelEvent } from './PixelEvent';
|
|
7
|
+
import { BeaconEvent } from './BeaconEvent';
|
|
8
|
+
import { BeaconType, BeaconCategory, } from './types';
|
|
9
|
+
const BATCH_TIMEOUT = 150;
|
|
10
|
+
const USERID_COOKIE_NAME = 'ssUserId';
|
|
11
|
+
const SHOPPERID_COOKIE_NAME = 'ssShopperId';
|
|
12
|
+
const COOKIE_EXPIRATION = 31536000000; // 1 year
|
|
13
|
+
const VIEWED_COOKIE_EXPIRATION = 220752000000; // 7 years
|
|
14
|
+
const COOKIE_SAMESITE = 'Lax';
|
|
15
|
+
const SESSIONID_STORAGE_NAME = 'ssSessionIdNamespace';
|
|
16
|
+
const LOCALSTORAGE_BEACON_POOL_NAME = 'ssBeaconPool';
|
|
17
|
+
const CART_PRODUCTS = 'ssCartProducts';
|
|
18
|
+
const VIEWED_PRODUCTS = 'ssViewedProducts';
|
|
19
|
+
const MAX_VIEWED_COUNT = 15;
|
|
20
|
+
const defaultConfig = {
|
|
21
|
+
id: 'track',
|
|
22
|
+
};
|
|
23
|
+
export class Tracker {
|
|
24
|
+
constructor(globals, config) {
|
|
25
|
+
this.targeters = [];
|
|
26
|
+
this.track = {
|
|
27
|
+
event: (payload) => {
|
|
28
|
+
const event = {
|
|
29
|
+
type: payload?.type || BeaconType.CUSTOM,
|
|
30
|
+
category: payload?.category || BeaconCategory.CUSTOM,
|
|
31
|
+
context: payload?.context ? deepmerge(this.context, payload.context) : this.context,
|
|
32
|
+
event: payload.event,
|
|
33
|
+
pid: payload?.pid || undefined,
|
|
34
|
+
};
|
|
35
|
+
const beaconEvent = new BeaconEvent(event);
|
|
36
|
+
this.sendEvents([beaconEvent]);
|
|
37
|
+
return beaconEvent;
|
|
38
|
+
},
|
|
39
|
+
shopper: {
|
|
40
|
+
login: (data, siteId) => {
|
|
41
|
+
// sets shopperid if logged in
|
|
42
|
+
if (!featureFlags.cookies) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (!data.id) {
|
|
46
|
+
console.error('tracker.shopper.login event: requires a valid shopper ID parameter. Example: tracker.shopper.login({ id: "1234" })');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
let context = this.context;
|
|
50
|
+
if (siteId) {
|
|
51
|
+
context = deepmerge(context, {
|
|
52
|
+
context: {
|
|
53
|
+
website: {
|
|
54
|
+
trackingCode: siteId,
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
const storedShopperId = this.getShopperId();
|
|
60
|
+
data.id = `${data.id}`;
|
|
61
|
+
if (storedShopperId != data.id) {
|
|
62
|
+
// user's logged in id has changed, update shopperId cookie send login event
|
|
63
|
+
cookies.set(SHOPPERID_COOKIE_NAME, data.id, COOKIE_SAMESITE, COOKIE_EXPIRATION);
|
|
64
|
+
this.context.shopperId = data.id;
|
|
65
|
+
const payload = {
|
|
66
|
+
type: BeaconType.LOGIN,
|
|
67
|
+
category: BeaconCategory.PERSONALIZATION,
|
|
68
|
+
context,
|
|
69
|
+
event: {},
|
|
70
|
+
};
|
|
71
|
+
return this.track.event(payload);
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
},
|
|
75
|
+
product: {
|
|
76
|
+
view: (data, siteId) => {
|
|
77
|
+
if (!data?.sku && !data?.childSku) {
|
|
78
|
+
console.error('track.product.view event: requires a valid sku and/or childSku. \nExample: track.product.view({ sku: "product123", childSku: "product123_a" })');
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
81
|
+
let context = this.context;
|
|
82
|
+
if (siteId) {
|
|
83
|
+
context = deepmerge(context, {
|
|
84
|
+
context: {
|
|
85
|
+
website: {
|
|
86
|
+
trackingCode: siteId,
|
|
87
|
+
},
|
|
88
|
+
},
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
const payload = {
|
|
92
|
+
type: BeaconType.PRODUCT,
|
|
93
|
+
category: BeaconCategory.PAGEVIEW,
|
|
94
|
+
context,
|
|
95
|
+
event: {
|
|
96
|
+
sku: data?.sku ? `${data.sku}` : undefined,
|
|
97
|
+
childSku: data?.childSku ? `${data.childSku}` : undefined,
|
|
98
|
+
},
|
|
99
|
+
};
|
|
100
|
+
// save recently viewed products to cookie
|
|
101
|
+
const sku = data?.sku || data?.childSku;
|
|
102
|
+
if (sku) {
|
|
103
|
+
const lastViewedProducts = this.cookies.viewed.get();
|
|
104
|
+
const uniqueCartItems = Array.from(new Set([...lastViewedProducts, sku])).map((item) => item.trim());
|
|
105
|
+
cookies.set(VIEWED_PRODUCTS, uniqueCartItems.slice(0, MAX_VIEWED_COUNT).join(','), COOKIE_SAMESITE, VIEWED_COOKIE_EXPIRATION);
|
|
106
|
+
}
|
|
107
|
+
// legacy tracking
|
|
108
|
+
if (data?.sku) {
|
|
109
|
+
// only send sku to pixel tracker if present (don't send childSku)
|
|
110
|
+
new PixelEvent({
|
|
111
|
+
...payload,
|
|
112
|
+
event: {
|
|
113
|
+
sku: data.sku,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return this.track.event(payload);
|
|
118
|
+
},
|
|
119
|
+
click: (data, siteId) => {
|
|
120
|
+
if (!data?.intellisuggestData || !data?.intellisuggestSignature) {
|
|
121
|
+
console.error(`track.product.click event: object parameter requires a valid intellisuggestData and intellisuggestSignature. \nExample: track.click.product({ intellisuggestData: "eJwrTs4tNM9jYCjKTM8oYXDWdQ3TDTfUDbIwMDVjMARCYwMQSi_KTAEA9IQKWA", intellisuggestSignature: "9e46f9fd3253c267fefc298704e39084a6f8b8e47abefdee57277996b77d8e70" })`);
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
let context = this.context;
|
|
125
|
+
if (siteId) {
|
|
126
|
+
context = deepmerge(context, {
|
|
127
|
+
context: {
|
|
128
|
+
website: {
|
|
129
|
+
trackingCode: siteId,
|
|
130
|
+
},
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
const payload = {
|
|
135
|
+
type: BeaconType.CLICK,
|
|
136
|
+
category: BeaconCategory.INTERACTION,
|
|
137
|
+
context,
|
|
138
|
+
event: {
|
|
139
|
+
intellisuggestData: data.intellisuggestData,
|
|
140
|
+
intellisuggestSignature: data.intellisuggestSignature,
|
|
141
|
+
href: data?.href ? `${data.href}` : undefined,
|
|
142
|
+
},
|
|
143
|
+
};
|
|
144
|
+
// legacy tracking
|
|
145
|
+
new TrackEvent(payload);
|
|
146
|
+
return this.track.event(payload);
|
|
147
|
+
},
|
|
148
|
+
},
|
|
149
|
+
cart: {
|
|
150
|
+
view: (data, siteId) => {
|
|
151
|
+
if (!Array.isArray(data?.items) || !data?.items.length) {
|
|
152
|
+
console.error('track.view.cart event: parameter must be an array of cart items. \nExample: track.view.cart({ items: [{ sku: "product123", childSku: "product123_a", qty: "1", price: "9.99" }] })');
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
let context = this.context;
|
|
156
|
+
if (siteId) {
|
|
157
|
+
context = deepmerge(context, {
|
|
158
|
+
context: {
|
|
159
|
+
website: {
|
|
160
|
+
trackingCode: siteId,
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
const items = data.items.map((item, index) => {
|
|
166
|
+
if (!item?.qty || !item?.price || (!item?.sku && !item?.childSku)) {
|
|
167
|
+
console.error(`track.view.cart event: item ${item} at index ${index} requires a valid qty, price, and (sku and/or childSku.) \nExample: track.view.cart({ items: [{ sku: "product123", childSku: "product123_a", qty: "1", price: "9.99" }] })`);
|
|
168
|
+
return;
|
|
169
|
+
}
|
|
170
|
+
const product = {
|
|
171
|
+
qty: `${item.qty}`,
|
|
172
|
+
price: `${item.price}`,
|
|
173
|
+
};
|
|
174
|
+
if (item?.sku) {
|
|
175
|
+
product.sku = `${item.sku}`;
|
|
176
|
+
}
|
|
177
|
+
if (item?.childSku) {
|
|
178
|
+
product.childSku = `${item.childSku}`;
|
|
179
|
+
}
|
|
180
|
+
return product;
|
|
181
|
+
});
|
|
182
|
+
const payload = {
|
|
183
|
+
type: BeaconType.CART,
|
|
184
|
+
category: BeaconCategory.CARTVIEW,
|
|
185
|
+
context,
|
|
186
|
+
event: { items },
|
|
187
|
+
};
|
|
188
|
+
// save cart items to cookie
|
|
189
|
+
if (items.length) {
|
|
190
|
+
const products = items.map((item) => item.sku || item.childSku);
|
|
191
|
+
this.cookies.cart.add(products);
|
|
192
|
+
}
|
|
193
|
+
// legacy tracking
|
|
194
|
+
new PixelEvent(payload);
|
|
195
|
+
return this.track.event(payload);
|
|
196
|
+
},
|
|
197
|
+
},
|
|
198
|
+
order: {
|
|
199
|
+
transaction: (data, siteId) => {
|
|
200
|
+
if (!data?.items || !Array.isArray(data.items) || !data.items.length) {
|
|
201
|
+
console.error('track.order.transaction event: object parameter must contain `items` array of cart items. \nExample: order.transaction({ order: { id: "1001", total: "9.99", city: "Los Angeles", state: "CA", country: "US" }, items: [{ sku: "product123", childSku: "product123_a", qty: "1", price: "9.99" }] })');
|
|
202
|
+
return;
|
|
203
|
+
}
|
|
204
|
+
let context = this.context;
|
|
205
|
+
if (siteId) {
|
|
206
|
+
context = deepmerge(context, {
|
|
207
|
+
context: {
|
|
208
|
+
website: {
|
|
209
|
+
trackingCode: siteId,
|
|
210
|
+
},
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
const items = data.items.map((item, index) => {
|
|
215
|
+
if (!item?.qty || !item?.price || (!item?.sku && !item?.childSku)) {
|
|
216
|
+
console.error(`track.order.transaction event: object parameter \`items\`: item ${item} at index ${index} requires a valid qty, price, and (sku and/or childSku.) \nExample: order.view({ items: [{ sku: "product123", childSku: "product123_a", qty: "1", price: "9.99" }] })`);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const product = {
|
|
220
|
+
qty: `${item.qty}`,
|
|
221
|
+
price: `${item.price}`,
|
|
222
|
+
};
|
|
223
|
+
if (item?.sku) {
|
|
224
|
+
product.sku = `${item.sku}`;
|
|
225
|
+
}
|
|
226
|
+
if (item?.childSku) {
|
|
227
|
+
product.childSku = `${item.childSku}`;
|
|
228
|
+
}
|
|
229
|
+
return product;
|
|
230
|
+
});
|
|
231
|
+
const eventPayload = {
|
|
232
|
+
orderId: data?.order?.id ? `${data.order.id}` : undefined,
|
|
233
|
+
total: data?.order?.total ? `${data.order.total}` : undefined,
|
|
234
|
+
city: data?.order?.city ? `${data.order.city}` : undefined,
|
|
235
|
+
state: data?.order?.state ? `${data.order.state}` : undefined,
|
|
236
|
+
country: data?.order?.country ? `${data.order.country}` : undefined,
|
|
237
|
+
items,
|
|
238
|
+
};
|
|
239
|
+
const payload = {
|
|
240
|
+
type: BeaconType.ORDER,
|
|
241
|
+
category: BeaconCategory.ORDERVIEW,
|
|
242
|
+
context,
|
|
243
|
+
event: eventPayload,
|
|
244
|
+
};
|
|
245
|
+
// clear cart items from cookie when order is placed
|
|
246
|
+
this.cookies.cart.clear();
|
|
247
|
+
// legacy tracking
|
|
248
|
+
new PixelEvent(payload);
|
|
249
|
+
return this.track.event(payload);
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
};
|
|
253
|
+
this.getUserId = () => {
|
|
254
|
+
let userId;
|
|
255
|
+
try {
|
|
256
|
+
userId = featureFlags.storage && window.localStorage.getItem(USERID_COOKIE_NAME);
|
|
257
|
+
if (featureFlags.cookies) {
|
|
258
|
+
userId = userId || cookies.get(USERID_COOKIE_NAME) || uuidv4();
|
|
259
|
+
cookies.set(USERID_COOKIE_NAME, userId, COOKIE_SAMESITE, COOKIE_EXPIRATION);
|
|
260
|
+
}
|
|
261
|
+
else if (!userId && featureFlags.storage) {
|
|
262
|
+
// if cookies are disabled, use localStorage instead
|
|
263
|
+
userId = uuidv4();
|
|
264
|
+
window.localStorage.setItem(USERID_COOKIE_NAME, userId);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
catch (e) {
|
|
268
|
+
console.error('Failed to persist user id to cookie or local storage:', e);
|
|
269
|
+
}
|
|
270
|
+
return userId;
|
|
271
|
+
};
|
|
272
|
+
this.getSessionId = () => {
|
|
273
|
+
let sessionId;
|
|
274
|
+
if (featureFlags.storage) {
|
|
275
|
+
try {
|
|
276
|
+
sessionId = window.sessionStorage.getItem(SESSIONID_STORAGE_NAME) || uuidv4();
|
|
277
|
+
window.sessionStorage.setItem(SESSIONID_STORAGE_NAME, sessionId);
|
|
278
|
+
featureFlags.cookies && cookies.set(SESSIONID_STORAGE_NAME, sessionId, COOKIE_SAMESITE, 0); //session cookie
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
console.error('Failed to persist session id to session storage:', e);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
else if (featureFlags.cookies) {
|
|
285
|
+
// use cookies if sessionStorage is not enabled and only reset cookie if new session to keep expiration
|
|
286
|
+
sessionId = cookies.get(SESSIONID_STORAGE_NAME);
|
|
287
|
+
if (!sessionId) {
|
|
288
|
+
sessionId = uuidv4();
|
|
289
|
+
cookies.set(SESSIONID_STORAGE_NAME, sessionId, COOKIE_SAMESITE, 0);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return sessionId;
|
|
293
|
+
};
|
|
294
|
+
this.getShopperId = () => {
|
|
295
|
+
const shopperId = cookies.get(SHOPPERID_COOKIE_NAME);
|
|
296
|
+
if (!shopperId) {
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
299
|
+
return shopperId;
|
|
300
|
+
};
|
|
301
|
+
this.cookies = {
|
|
302
|
+
cart: {
|
|
303
|
+
get: () => {
|
|
304
|
+
const items = cookies.get(CART_PRODUCTS);
|
|
305
|
+
if (!items) {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
return items.split(',');
|
|
309
|
+
},
|
|
310
|
+
set: (items) => {
|
|
311
|
+
if (items.length) {
|
|
312
|
+
const cartItems = items.map((item) => item.trim());
|
|
313
|
+
const uniqueCartItems = Array.from(new Set(cartItems));
|
|
314
|
+
cookies.set(CART_PRODUCTS, uniqueCartItems.join(','), COOKIE_SAMESITE, 0);
|
|
315
|
+
}
|
|
316
|
+
},
|
|
317
|
+
add: (items) => {
|
|
318
|
+
if (items.length) {
|
|
319
|
+
const currentCartItems = this.cookies.cart.get();
|
|
320
|
+
const itemsToAdd = items.map((item) => item.trim());
|
|
321
|
+
const uniqueCartItems = Array.from(new Set([...currentCartItems, ...itemsToAdd]));
|
|
322
|
+
cookies.set(CART_PRODUCTS, uniqueCartItems.join(','), COOKIE_SAMESITE, 0);
|
|
323
|
+
}
|
|
324
|
+
},
|
|
325
|
+
remove: (items) => {
|
|
326
|
+
if (items.length) {
|
|
327
|
+
const currentCartItems = this.cookies.cart.get();
|
|
328
|
+
const itemsToRemove = items.map((item) => item.trim());
|
|
329
|
+
const updatedItems = currentCartItems.filter((item) => !itemsToRemove.includes(item));
|
|
330
|
+
cookies.set(CART_PRODUCTS, updatedItems.join(','), COOKIE_SAMESITE, 0);
|
|
331
|
+
}
|
|
332
|
+
},
|
|
333
|
+
clear: () => {
|
|
334
|
+
cookies.unset(CART_PRODUCTS);
|
|
335
|
+
},
|
|
336
|
+
},
|
|
337
|
+
viewed: {
|
|
338
|
+
get: () => {
|
|
339
|
+
const items = cookies.get(VIEWED_PRODUCTS);
|
|
340
|
+
if (!items) {
|
|
341
|
+
return [];
|
|
342
|
+
}
|
|
343
|
+
return items.split(',');
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
this.sendEvents = (eventsToSend) => {
|
|
348
|
+
const events = JSON.parse(this.localStorage.get(LOCALSTORAGE_BEACON_POOL_NAME) || '[]');
|
|
349
|
+
if (eventsToSend) {
|
|
350
|
+
eventsToSend.forEach((event) => {
|
|
351
|
+
events.push({ ...event });
|
|
352
|
+
});
|
|
353
|
+
this.localStorage.set(LOCALSTORAGE_BEACON_POOL_NAME, JSON.stringify(events));
|
|
354
|
+
}
|
|
355
|
+
clearTimeout(this.isSending);
|
|
356
|
+
this.isSending = window.setTimeout(() => {
|
|
357
|
+
if (events.length) {
|
|
358
|
+
const xhr = new XMLHttpRequest();
|
|
359
|
+
xhr.open('POST', 'https://beacon.searchspring.io/beacon');
|
|
360
|
+
xhr.setRequestHeader('Content-Type', 'application/json');
|
|
361
|
+
xhr.send(JSON.stringify(events.length == 1 ? events[0] : events));
|
|
362
|
+
}
|
|
363
|
+
this.localStorage.set(LOCALSTORAGE_BEACON_POOL_NAME, JSON.stringify([]));
|
|
364
|
+
}, BATCH_TIMEOUT);
|
|
365
|
+
};
|
|
366
|
+
if (typeof globals != 'object' || typeof globals.siteId != 'string') {
|
|
367
|
+
throw new Error(`Invalid config passed to tracker. The "siteId" attribute must be provided.`);
|
|
368
|
+
}
|
|
369
|
+
this.config = deepmerge(defaultConfig, config || {});
|
|
370
|
+
this.globals = globals;
|
|
371
|
+
this.localStorage = new StorageStore({
|
|
372
|
+
type: StorageType.LOCAL,
|
|
373
|
+
key: `ss-${this.config.id}-${this.globals.siteId}-local`,
|
|
374
|
+
});
|
|
375
|
+
this.context = {
|
|
376
|
+
userId: this.getUserId(),
|
|
377
|
+
sessionId: this.getSessionId(),
|
|
378
|
+
shopperId: this.getShopperId(),
|
|
379
|
+
pageLoadId: uuidv4(),
|
|
380
|
+
website: {
|
|
381
|
+
trackingCode: this.globals.siteId,
|
|
382
|
+
},
|
|
383
|
+
};
|
|
384
|
+
if (!window.searchspring?.tracker) {
|
|
385
|
+
window.searchspring = window.searchspring || {};
|
|
386
|
+
window.searchspring.tracker = this;
|
|
387
|
+
window.searchspring.version = version;
|
|
388
|
+
}
|
|
389
|
+
// one targeter to rule them all
|
|
390
|
+
this.targeters.push(new DomTargeter([{ selector: 'script[type^="searchspring/track/"]', emptyTarget: false }], (target, elem) => {
|
|
391
|
+
const { item, items, siteId, shopper, order, type } = getContext(['item', 'items', 'siteId', 'shopper', 'order'], elem);
|
|
392
|
+
switch (type) {
|
|
393
|
+
case 'searchspring/track/shopper/login':
|
|
394
|
+
this.track.shopper.login(shopper, siteId);
|
|
395
|
+
break;
|
|
396
|
+
case 'searchspring/track/product/view':
|
|
397
|
+
this.track.product.view(item, siteId);
|
|
398
|
+
break;
|
|
399
|
+
case 'searchspring/track/cart/view':
|
|
400
|
+
this.track.cart.view({ items }, siteId);
|
|
401
|
+
break;
|
|
402
|
+
case 'searchspring/track/order/transaction':
|
|
403
|
+
this.track.order.transaction({ order, items }, siteId);
|
|
404
|
+
break;
|
|
405
|
+
default:
|
|
406
|
+
console.error(`${type} event is not supported or incorrect`);
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}));
|
|
410
|
+
document.addEventListener('click', (event) => {
|
|
411
|
+
event.preventDefault();
|
|
412
|
+
const attributes = {};
|
|
413
|
+
Object.values(event.target.attributes).forEach((attr) => {
|
|
414
|
+
attributes[attr.nodeName] = event.target.getAttribute(attr.nodeName);
|
|
415
|
+
});
|
|
416
|
+
const updateRecsControllers = () => {
|
|
417
|
+
if (window.searchspring.controller) {
|
|
418
|
+
Object.keys(window.searchspring.controller).forEach((name) => {
|
|
419
|
+
const controller = window.searchspring.controller[name];
|
|
420
|
+
if (controller.type === 'recommendation' && controller.config?.realtime) {
|
|
421
|
+
controller.search();
|
|
422
|
+
}
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
};
|
|
426
|
+
if (attributes[`ss-${this.config.id}-cart-add`]) {
|
|
427
|
+
// add skus to cart
|
|
428
|
+
const skus = attributes[`ss-${this.config.id}-cart-add`].split(',');
|
|
429
|
+
this.cookies.cart.add(skus);
|
|
430
|
+
updateRecsControllers();
|
|
431
|
+
}
|
|
432
|
+
else if (attributes[`ss-${this.config.id}-cart-remove`]) {
|
|
433
|
+
// remove skus from cart
|
|
434
|
+
const skus = attributes[`ss-${this.config.id}-cart-remove`].split(',');
|
|
435
|
+
this.cookies.cart.remove(skus);
|
|
436
|
+
updateRecsControllers();
|
|
437
|
+
}
|
|
438
|
+
else if (`ss-${this.config.id}-cart-clear` in attributes) {
|
|
439
|
+
// clear all from cart
|
|
440
|
+
this.cookies.cart.clear();
|
|
441
|
+
updateRecsControllers();
|
|
442
|
+
}
|
|
443
|
+
else if (attributes[`ss-${this.config.id}-intellisuggest`] && attributes[`ss-${this.config.id}-intellisuggest-signature`]) {
|
|
444
|
+
// product click
|
|
445
|
+
const intellisuggestData = attributes[`ss-${this.config.id}-intellisuggest`];
|
|
446
|
+
const intellisuggestSignature = attributes[`ss-${this.config.id}-intellisuggest-signature`];
|
|
447
|
+
const href = attributes['href'];
|
|
448
|
+
this.track.product.click({
|
|
449
|
+
intellisuggestData,
|
|
450
|
+
intellisuggestSignature,
|
|
451
|
+
href,
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
});
|
|
455
|
+
this.sendEvents();
|
|
456
|
+
}
|
|
457
|
+
retarget() {
|
|
458
|
+
this.targeters.forEach((target) => {
|
|
459
|
+
target.retarget();
|
|
460
|
+
});
|
|
461
|
+
}
|
|
462
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,YAAY,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAEjD,cAAc,SAAS,CAAC"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import { BeaconEvent } from './BeaconEvent';
|
|
2
|
+
export declare type TrackerGlobals = {
|
|
3
|
+
siteId: string;
|
|
4
|
+
};
|
|
5
|
+
export declare type TrackerConfig = {
|
|
6
|
+
id: string;
|
|
7
|
+
};
|
|
8
|
+
export interface BeaconPayload {
|
|
9
|
+
type: BeaconType;
|
|
10
|
+
category: BeaconCategory;
|
|
11
|
+
context?: BeaconContext;
|
|
12
|
+
meta?: BeaconMeta;
|
|
13
|
+
event: ProductViewEvent | CartViewEvent | OrderTransactionEvent | RecommendationsEvent | ProductClickEvent | CustomBeaconEvent | Record<string, never>;
|
|
14
|
+
id?: string;
|
|
15
|
+
pid?: string | null;
|
|
16
|
+
}
|
|
17
|
+
export declare enum BeaconType {
|
|
18
|
+
PRODUCT = "product",
|
|
19
|
+
CART = "cart",
|
|
20
|
+
ORDER = "transaction",
|
|
21
|
+
LOGIN = "login",
|
|
22
|
+
CLICK = "click",
|
|
23
|
+
CUSTOM = "custom",
|
|
24
|
+
/** For Profiles Recommendations */
|
|
25
|
+
PROFILE_RENDER = "profile.render",
|
|
26
|
+
PROFILE_IMPRESSION = "profile.impression",
|
|
27
|
+
PROFILE_CLICK = "profile.click",
|
|
28
|
+
/** For Recommended Products within a Profile */
|
|
29
|
+
PROFILE_PRODUCT_RENDER = "profile.product.render",
|
|
30
|
+
PROFILE_PRODUCT_IMPRESSION = "profile.product.impression",
|
|
31
|
+
PROFILE_PRODUCT_CLICK = "profile.product.click"
|
|
32
|
+
}
|
|
33
|
+
export declare enum BeaconCategory {
|
|
34
|
+
PAGEVIEW = "searchspring.page.view",
|
|
35
|
+
CARTVIEW = "searchspring.shop.cart",
|
|
36
|
+
ORDERVIEW = "searchspring.shop.transaction",
|
|
37
|
+
PERSONALIZATION = "searchspring.personalization",
|
|
38
|
+
RECOMMENDATIONS = "searchspring.recommendations.user-interactions",
|
|
39
|
+
INTERACTION = "searchspring.user-interactions",
|
|
40
|
+
CUSTOM = "custom"
|
|
41
|
+
}
|
|
42
|
+
export interface BeaconContext {
|
|
43
|
+
userId?: string;
|
|
44
|
+
pageLoadId?: string;
|
|
45
|
+
sessionId?: string;
|
|
46
|
+
shopperId?: string;
|
|
47
|
+
website: {
|
|
48
|
+
trackingCode: string;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface BeaconMeta {
|
|
52
|
+
initiator: {
|
|
53
|
+
lib: string;
|
|
54
|
+
'lib.version': string;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
export interface ShopperLoginEvent {
|
|
58
|
+
id: string;
|
|
59
|
+
}
|
|
60
|
+
export interface ProductViewEvent {
|
|
61
|
+
sku?: string;
|
|
62
|
+
childSku?: string;
|
|
63
|
+
}
|
|
64
|
+
export interface CartViewEvent {
|
|
65
|
+
items: Product[];
|
|
66
|
+
}
|
|
67
|
+
export interface Product extends ProductViewEvent {
|
|
68
|
+
qty: string | number;
|
|
69
|
+
price: string | number;
|
|
70
|
+
}
|
|
71
|
+
export interface OrderTransactionEvent {
|
|
72
|
+
orderId?: string | number;
|
|
73
|
+
total?: string | number;
|
|
74
|
+
city?: string;
|
|
75
|
+
state?: string;
|
|
76
|
+
country?: string;
|
|
77
|
+
items: Product[];
|
|
78
|
+
}
|
|
79
|
+
export interface OrderTransactionData {
|
|
80
|
+
order?: {
|
|
81
|
+
id?: string | number;
|
|
82
|
+
total?: string | number;
|
|
83
|
+
city?: string;
|
|
84
|
+
state?: string;
|
|
85
|
+
country?: string;
|
|
86
|
+
};
|
|
87
|
+
items: Product[];
|
|
88
|
+
}
|
|
89
|
+
export interface RecommendationsEvent {
|
|
90
|
+
profile?: {
|
|
91
|
+
tag?: string;
|
|
92
|
+
placement?: string;
|
|
93
|
+
seed?: ProductViewEvent[];
|
|
94
|
+
};
|
|
95
|
+
product?: {
|
|
96
|
+
id?: string;
|
|
97
|
+
mappings?: {
|
|
98
|
+
core?: {
|
|
99
|
+
sku?: string;
|
|
100
|
+
name?: string;
|
|
101
|
+
url?: string;
|
|
102
|
+
thumbnailImageUrl?: string;
|
|
103
|
+
price?: number;
|
|
104
|
+
msrp?: number;
|
|
105
|
+
};
|
|
106
|
+
};
|
|
107
|
+
seed?: ProductViewEvent[];
|
|
108
|
+
};
|
|
109
|
+
context?: {
|
|
110
|
+
type?: string;
|
|
111
|
+
tag?: string;
|
|
112
|
+
placement?: string;
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
export interface ProductClickEvent {
|
|
116
|
+
intellisuggestData: string;
|
|
117
|
+
intellisuggestSignature: string;
|
|
118
|
+
href?: string;
|
|
119
|
+
}
|
|
120
|
+
export interface CustomBeaconEvent {
|
|
121
|
+
[key: string]: any;
|
|
122
|
+
}
|
|
123
|
+
export interface TrackMethods {
|
|
124
|
+
event: (payload: BeaconPayload) => BeaconEvent;
|
|
125
|
+
shopper: {
|
|
126
|
+
login: (data: ShopperLoginEvent, siteId?: string) => BeaconEvent;
|
|
127
|
+
};
|
|
128
|
+
product: {
|
|
129
|
+
view: (data: ProductViewEvent, siteId?: string) => BeaconEvent;
|
|
130
|
+
click: (data: ProductClickEvent, siteId?: string) => BeaconEvent;
|
|
131
|
+
};
|
|
132
|
+
cart: {
|
|
133
|
+
view: (data: CartViewEvent, siteId?: string) => BeaconEvent;
|
|
134
|
+
};
|
|
135
|
+
order: {
|
|
136
|
+
transaction: (data: OrderTransactionData, siteId?: string) => BeaconEvent;
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
declare global {
|
|
140
|
+
interface Window {
|
|
141
|
+
searchspring?: any;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/types.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export var BeaconType;
|
|
2
|
+
(function (BeaconType) {
|
|
3
|
+
BeaconType["PRODUCT"] = "product";
|
|
4
|
+
BeaconType["CART"] = "cart";
|
|
5
|
+
BeaconType["ORDER"] = "transaction";
|
|
6
|
+
BeaconType["LOGIN"] = "login";
|
|
7
|
+
BeaconType["CLICK"] = "click";
|
|
8
|
+
BeaconType["CUSTOM"] = "custom";
|
|
9
|
+
/** For Profiles Recommendations */
|
|
10
|
+
BeaconType["PROFILE_RENDER"] = "profile.render";
|
|
11
|
+
BeaconType["PROFILE_IMPRESSION"] = "profile.impression";
|
|
12
|
+
BeaconType["PROFILE_CLICK"] = "profile.click";
|
|
13
|
+
/** For Recommended Products within a Profile */
|
|
14
|
+
BeaconType["PROFILE_PRODUCT_RENDER"] = "profile.product.render";
|
|
15
|
+
BeaconType["PROFILE_PRODUCT_IMPRESSION"] = "profile.product.impression";
|
|
16
|
+
BeaconType["PROFILE_PRODUCT_CLICK"] = "profile.product.click";
|
|
17
|
+
})(BeaconType || (BeaconType = {}));
|
|
18
|
+
export var BeaconCategory;
|
|
19
|
+
(function (BeaconCategory) {
|
|
20
|
+
BeaconCategory["PAGEVIEW"] = "searchspring.page.view";
|
|
21
|
+
BeaconCategory["CARTVIEW"] = "searchspring.shop.cart";
|
|
22
|
+
BeaconCategory["ORDERVIEW"] = "searchspring.shop.transaction";
|
|
23
|
+
BeaconCategory["PERSONALIZATION"] = "searchspring.personalization";
|
|
24
|
+
BeaconCategory["RECOMMENDATIONS"] = "searchspring.recommendations.user-interactions";
|
|
25
|
+
BeaconCategory["INTERACTION"] = "searchspring.user-interactions";
|
|
26
|
+
BeaconCategory["CUSTOM"] = "custom";
|
|
27
|
+
})(BeaconCategory || (BeaconCategory = {}));
|