choreograph-create-pixel 1.4.1 → 1.4.3

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/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Choreograph Create Pixel
2
2
 
3
- This library lets you apply best practises to [Create](https://www.lemonpi.io/) pixel development and implementation.
3
+ This library lets you apply best practises to [Create](https://create.choreograph.com/) pixel development and implementation.
4
4
 
5
5
  ## Features
6
6
 
@@ -11,25 +11,25 @@ This library lets you apply best practises to [Create](https://www.lemonpi.io/)
11
11
  - [x] User interaction triggers
12
12
  - [x] [Console debugger](#debugging)
13
13
 
14
- ### Scraper pixels
14
+ ### Scraper pixel
15
15
 
16
16
  <small>Type: `scraper`</small>
17
17
 
18
- Scraper pixels are used to scrape (collect) data from websites, and store that data as products within an advertiser's product store.
18
+ A scraper pixel is used to scrape (collect) data from websites, and store that data as products within an advertiser's product store.
19
19
 
20
20
  ### Segment pixels
21
21
 
22
22
  <small>Types: `viewed`, `basketed` and `purchased`</small>
23
23
 
24
- Products in ads are shown by recommendation. Segment pixels determine this recommendation on a consumer level, by storing a **cookie** at different moments or events during the consumer's journey.
24
+ Products in ads are sorted by recommendation. Segment pixels determine this recommendation on a consumer level, by storing a **cookie** at different moments or events during the consumer's journey.
25
25
 
26
26
  ### Post-click pixels
27
27
 
28
28
  <small>Types: `attribution` and `conversion`</small>
29
29
 
30
- Post-click conversion attribution tracks which clicked creative variant attributed to a website conversion. The **attribution** pixel collects the creative's impression ID from the URL (`ccpid` query parameter) on the landing page, and the **conversion** pixel logs this ID as a performance metric in Create when a user performed a conversion, like a purchase.
30
+ Post-click conversion attribution tracks which clicked creative variant from an ad attributed to a website conversion. The **attribution** pixel collects and stores (through [`localStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage)) the creative's impression ID from the landing URL's `ccpid` query parameter, and the **conversion** pixel logs this ID as a performance metric in Create whenever a user performed a conversion, like a purchase.
31
31
 
32
- ## Quickstart
32
+ ## Quick start
33
33
 
34
34
  The following theoretical snippet includes all pixel types, can be embedded on every page of _example.com_, and will automatically determine which pixel to enable and disable through continuous URL validation.
35
35
 
@@ -38,7 +38,7 @@ The following theoretical snippet includes all pixel types, can be embedded on e
38
38
  ```js
39
39
  import Pixel from "choreograph-create-pixel";
40
40
 
41
- // Scraper pixel
41
+ // Scraper pixel example
42
42
  new Pixel({
43
43
  advertiser: 0,
44
44
  type: "scraper",
@@ -59,7 +59,7 @@ new Pixel({
59
59
  },
60
60
  });
61
61
 
62
- // Viewed segment pixel
62
+ // Viewed segment pixel example
63
63
  new Pixel({
64
64
  advertiser: 0,
65
65
  type: "viewed",
@@ -69,7 +69,7 @@ new Pixel({
69
69
  scrape: () => window.dataLayer[0].product.sku,
70
70
  });
71
71
 
72
- // Basketed segment pixel
72
+ // Basketed segment pixel example
73
73
  new Pixel({
74
74
  advertiser: 0,
75
75
  type: "basketed",
@@ -85,7 +85,7 @@ new Pixel({
85
85
  scrape: (element) => element.getAttribute("data-sku"),
86
86
  });
87
87
 
88
- // Purchased segment pixel
88
+ // Purchased segment pixel example
89
89
  new Pixel({
90
90
  advertiser: 0,
91
91
  type: "purchased",
@@ -100,7 +100,7 @@ new Pixel({
100
100
  scrape: () => window.dataLayer[0].cart.map((product) => product.sku),
101
101
  });
102
102
 
103
- // Attribution post-click pixel
103
+ // Attribution post-click pixel example
104
104
  new Pixel({
105
105
  advertiser: 0,
106
106
  type: "attribution",
@@ -112,7 +112,7 @@ new Pixel({
112
112
  url: /example\.com\/products\/\d/,
113
113
  });
114
114
 
115
- // Conversion post-click pixel
115
+ // Conversion post-click pixel example
116
116
  new Pixel({
117
117
  advertiser: 0,
118
118
  type: "conversion",
@@ -1,4 +1,4 @@
1
- /*! choreograph-create-pixel v1.4.1 2023/02/01 */
1
+ /*! choreograph-create-pixel v1.4.3 2024/08/27 */
2
2
  'use strict';
3
3
 
4
4
  class ChoreographCreatePixel {
@@ -85,8 +85,8 @@ class ChoreographCreatePixel {
85
85
  if (!this.config.debug || this.logs.includes(message)) return;
86
86
 
87
87
  const args = [
88
- `%c⸬ create%c ${this.config.icons[this.config.type]} ${this.config.type}${
89
- this.config.label ? ` (${this.config.label})` : ""
88
+ `%c⸬ create%c ${this.config.icons[this.config.type]} ${
89
+ this.config.type
90
90
  } %c${message}`,
91
91
  "background:black;color:white;border-radius:3px;padding:3px 6px",
92
92
  "font-weight:bold",
@@ -196,6 +196,7 @@ class ChoreographCreatePixel {
196
196
  }
197
197
  }
198
198
 
199
+ // TO-DO: Move this to each method's end? Pageview conversions are currently recursive
199
200
  setTimeout(() => this.cycle(), 750);
200
201
  }
201
202
 
@@ -211,17 +212,23 @@ class ChoreographCreatePixel {
211
212
 
212
213
  const storageItemLabel = `choreograph-${this.config.label}`;
213
214
  const storedCcpid = localStorage.getItem(storageItemLabel);
215
+
216
+ if (storedCcpid === "")
217
+ return this.log("warn", `"${this.config.label}" already converted`);
218
+
214
219
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
215
- this.log("success", "Successful!", ccpid);
220
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
216
221
  }
217
222
 
218
223
  convert() {
219
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
224
+ const label = `choreograph-${this.config.label}`;
225
+ const ccpid = localStorage.getItem(label);
220
226
 
221
227
  if (!ccpid)
222
228
  return this.log("warn", `"${this.config.label}" attribution not present`);
223
229
 
224
230
  this.send(ccpid);
231
+ localStorage.setItem(label, "");
225
232
  }
226
233
 
227
234
  scrape(element) {
@@ -294,113 +301,128 @@ class ChoreographCreatePixel {
294
301
  }
295
302
 
296
303
  send(data) {
304
+ let payload;
297
305
  let url;
306
+ let method = "GET";
298
307
 
299
308
  switch (this.config.type) {
300
- case "scraper":
309
+ case "scraper": {
310
+ const { sku } = data;
311
+ delete data.sku;
312
+
313
+ payload = {
314
+ "advertiser-id": this.config.advertiser,
315
+ sku,
316
+ fields: data,
317
+ };
318
+
301
319
  url = `https://d.lemonpi.io/scrapes${
302
320
  this.config.debug ? "?validate=true" : ""
303
321
  }`;
304
322
 
323
+ method = "POST";
305
324
  break;
325
+ }
306
326
 
307
- case "conversion":
308
- url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
309
- JSON.stringify({
310
- version: 1,
311
- type: "conversion",
312
- name: this.config.label,
313
- "conversion-attribution-id": data,
314
- "advertiser-id": this.config.advertiser,
315
- })
316
- )}`;
317
-
318
- break;
327
+ case "viewed":
328
+ case "basketed":
329
+ case "purchased":
330
+ payload = {
331
+ "event-type": `product-${this.config.type}`,
332
+ sku: data,
333
+ };
319
334
 
320
- default:
321
335
  url = `https://d.lemonpi.io/a/${
322
336
  this.config.advertiser
323
- }/product/event?e=${encodeURIComponent(
324
- JSON.stringify({
325
- "event-type": `product-${this.config.type}`,
326
- sku: data,
327
- })
337
+ }/product/event?e=${encodeURIComponent(JSON.stringify(payload))}`;
338
+
339
+ break;
340
+
341
+ case "conversion":
342
+ payload = {
343
+ version: 1,
344
+ type: "conversion",
345
+ name: this.config.label,
346
+ "conversion-attribution-id": data,
347
+ "advertiser-id": this.config.advertiser,
348
+ };
349
+
350
+ url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
351
+ JSON.stringify(payload)
328
352
  )}`;
329
353
 
330
354
  break;
331
355
  }
332
356
 
333
- if (this.config.type !== "scraper" && !this.config.debug)
357
+ if (
358
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
359
+ !this.config.debug
360
+ ) {
334
361
  new Image().src = url;
335
- else
336
- fetch(
337
- url,
338
- this.config.type === "scraper"
339
- ? {
340
- method: "POST",
341
- headers: { "Content-Type": "application/json" },
342
- body: JSON.stringify({
343
- "advertiser-id": this.config.advertiser,
344
- sku: data.sku,
345
- fields: { ...data, sku: undefined },
346
- }),
347
- }
348
- : null
362
+ return;
363
+ }
364
+
365
+ fetch(
366
+ url,
367
+ method === "POST"
368
+ ? {
369
+ method: "POST",
370
+ headers: { "Content-Type": "application/json" },
371
+ body: JSON.stringify(payload),
372
+ }
373
+ : null
374
+ )
375
+ .then((response) =>
376
+ response
377
+ .json()
378
+ .then((result) => {
379
+ if (response.ok) {
380
+ this.log("warn", "Successful, with warnings. Details:", {
381
+ payload,
382
+ result,
383
+ });
384
+
385
+ try {
386
+ this.config.after(data);
387
+ } catch (error) {
388
+ this.log("error", error.message, {
389
+ after: this.config.after,
390
+ });
391
+ }
392
+ } else
393
+ this.log(
394
+ "error",
395
+ `Failed: ${response.status} (${response.statusText}). Details:`,
396
+ { payload, result }
397
+ );
398
+
399
+ this.logs.length = 0;
400
+ })
401
+ .catch(() => {
402
+ if (response.ok) {
403
+ this.log("success", "Successful!", { payload });
404
+
405
+ try {
406
+ this.config.after(data);
407
+ } catch (error) {
408
+ this.log("error", error.message, {
409
+ after: this.config.after,
410
+ });
411
+ }
412
+ } else
413
+ this.log(
414
+ "error",
415
+ `Failed: ${response.status} (${response.statusText}). Details:`,
416
+ { payload, response }
417
+ );
418
+
419
+ this.logs.length = 0;
420
+ })
349
421
  )
350
- .then((response) =>
351
- response
352
- .json()
353
- .then((result) => {
354
- if (response.ok) {
355
- this.log("warn", "Successful, with warnings. Details:", result);
356
-
357
- try {
358
- this.config.after(data);
359
- } catch (error) {
360
- this.log("error", error.message, {
361
- after: this.config.after,
362
- });
363
- }
364
- } else
365
- this.log(
366
- "error",
367
- `Failed: ${response.status} (${response.statusText}). Details:`,
368
- result
369
- );
370
-
371
- this.logs.length = 0;
372
- })
373
- .catch(() => {
374
- if (response.ok) {
375
- this.log(
376
- "success",
377
- "Successful!",
378
- ["viewed", "basketed", "purchased"].includes(this.config.type)
379
- ? { sku: data }
380
- : data
381
- );
382
-
383
- try {
384
- this.config.after(data);
385
- } catch (error) {
386
- this.log("error", error.message, {
387
- after: this.config.after,
388
- });
389
- }
390
- } else
391
- this.log(
392
- "error",
393
- `Failed: ${response.status} (${response.statusText}). Details:`,
394
- response
395
- );
396
-
397
- this.logs.length = 0;
398
- })
399
- )
400
- .catch((error) => {
401
- this.log("error", `Failed: ${error.message}`);
402
- this.logs.length = 0;
403
- });
422
+ .catch((error) => {
423
+ this.log("error", `Failed: ${error.message}`, { payload });
424
+ this.logs.length = 0;
425
+ });
404
426
  }
405
427
  }
406
428
 
@@ -1,4 +1,4 @@
1
- /*! choreograph-create-pixel v1.4.1 2023/02/01 */
1
+ /*! choreograph-create-pixel v1.4.3 2024/08/27 */
2
2
  class ChoreographCreatePixel {
3
3
  constructor(userConfig) {
4
4
  this.previouslyScrapedHash = null;
@@ -83,8 +83,8 @@ class ChoreographCreatePixel {
83
83
  if (!this.config.debug || this.logs.includes(message)) return;
84
84
 
85
85
  const args = [
86
- `%c⸬ create%c ${this.config.icons[this.config.type]} ${this.config.type}${
87
- this.config.label ? ` (${this.config.label})` : ""
86
+ `%c⸬ create%c ${this.config.icons[this.config.type]} ${
87
+ this.config.type
88
88
  } %c${message}`,
89
89
  "background:black;color:white;border-radius:3px;padding:3px 6px",
90
90
  "font-weight:bold",
@@ -194,6 +194,7 @@ class ChoreographCreatePixel {
194
194
  }
195
195
  }
196
196
 
197
+ // TO-DO: Move this to each method's end? Pageview conversions are currently recursive
197
198
  setTimeout(() => this.cycle(), 750);
198
199
  }
199
200
 
@@ -209,17 +210,23 @@ class ChoreographCreatePixel {
209
210
 
210
211
  const storageItemLabel = `choreograph-${this.config.label}`;
211
212
  const storedCcpid = localStorage.getItem(storageItemLabel);
213
+
214
+ if (storedCcpid === "")
215
+ return this.log("warn", `"${this.config.label}" already converted`);
216
+
212
217
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
213
- this.log("success", "Successful!", ccpid);
218
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
214
219
  }
215
220
 
216
221
  convert() {
217
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
222
+ const label = `choreograph-${this.config.label}`;
223
+ const ccpid = localStorage.getItem(label);
218
224
 
219
225
  if (!ccpid)
220
226
  return this.log("warn", `"${this.config.label}" attribution not present`);
221
227
 
222
228
  this.send(ccpid);
229
+ localStorage.setItem(label, "");
223
230
  }
224
231
 
225
232
  scrape(element) {
@@ -292,113 +299,128 @@ class ChoreographCreatePixel {
292
299
  }
293
300
 
294
301
  send(data) {
302
+ let payload;
295
303
  let url;
304
+ let method = "GET";
296
305
 
297
306
  switch (this.config.type) {
298
- case "scraper":
307
+ case "scraper": {
308
+ const { sku } = data;
309
+ delete data.sku;
310
+
311
+ payload = {
312
+ "advertiser-id": this.config.advertiser,
313
+ sku,
314
+ fields: data,
315
+ };
316
+
299
317
  url = `https://d.lemonpi.io/scrapes${
300
318
  this.config.debug ? "?validate=true" : ""
301
319
  }`;
302
320
 
321
+ method = "POST";
303
322
  break;
323
+ }
304
324
 
305
- case "conversion":
306
- url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
307
- JSON.stringify({
308
- version: 1,
309
- type: "conversion",
310
- name: this.config.label,
311
- "conversion-attribution-id": data,
312
- "advertiser-id": this.config.advertiser,
313
- })
314
- )}`;
315
-
316
- break;
325
+ case "viewed":
326
+ case "basketed":
327
+ case "purchased":
328
+ payload = {
329
+ "event-type": `product-${this.config.type}`,
330
+ sku: data,
331
+ };
317
332
 
318
- default:
319
333
  url = `https://d.lemonpi.io/a/${
320
334
  this.config.advertiser
321
- }/product/event?e=${encodeURIComponent(
322
- JSON.stringify({
323
- "event-type": `product-${this.config.type}`,
324
- sku: data,
325
- })
335
+ }/product/event?e=${encodeURIComponent(JSON.stringify(payload))}`;
336
+
337
+ break;
338
+
339
+ case "conversion":
340
+ payload = {
341
+ version: 1,
342
+ type: "conversion",
343
+ name: this.config.label,
344
+ "conversion-attribution-id": data,
345
+ "advertiser-id": this.config.advertiser,
346
+ };
347
+
348
+ url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
349
+ JSON.stringify(payload)
326
350
  )}`;
327
351
 
328
352
  break;
329
353
  }
330
354
 
331
- if (this.config.type !== "scraper" && !this.config.debug)
355
+ if (
356
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
357
+ !this.config.debug
358
+ ) {
332
359
  new Image().src = url;
333
- else
334
- fetch(
335
- url,
336
- this.config.type === "scraper"
337
- ? {
338
- method: "POST",
339
- headers: { "Content-Type": "application/json" },
340
- body: JSON.stringify({
341
- "advertiser-id": this.config.advertiser,
342
- sku: data.sku,
343
- fields: { ...data, sku: undefined },
344
- }),
345
- }
346
- : null
360
+ return;
361
+ }
362
+
363
+ fetch(
364
+ url,
365
+ method === "POST"
366
+ ? {
367
+ method: "POST",
368
+ headers: { "Content-Type": "application/json" },
369
+ body: JSON.stringify(payload),
370
+ }
371
+ : null
372
+ )
373
+ .then((response) =>
374
+ response
375
+ .json()
376
+ .then((result) => {
377
+ if (response.ok) {
378
+ this.log("warn", "Successful, with warnings. Details:", {
379
+ payload,
380
+ result,
381
+ });
382
+
383
+ try {
384
+ this.config.after(data);
385
+ } catch (error) {
386
+ this.log("error", error.message, {
387
+ after: this.config.after,
388
+ });
389
+ }
390
+ } else
391
+ this.log(
392
+ "error",
393
+ `Failed: ${response.status} (${response.statusText}). Details:`,
394
+ { payload, result }
395
+ );
396
+
397
+ this.logs.length = 0;
398
+ })
399
+ .catch(() => {
400
+ if (response.ok) {
401
+ this.log("success", "Successful!", { payload });
402
+
403
+ try {
404
+ this.config.after(data);
405
+ } catch (error) {
406
+ this.log("error", error.message, {
407
+ after: this.config.after,
408
+ });
409
+ }
410
+ } else
411
+ this.log(
412
+ "error",
413
+ `Failed: ${response.status} (${response.statusText}). Details:`,
414
+ { payload, response }
415
+ );
416
+
417
+ this.logs.length = 0;
418
+ })
347
419
  )
348
- .then((response) =>
349
- response
350
- .json()
351
- .then((result) => {
352
- if (response.ok) {
353
- this.log("warn", "Successful, with warnings. Details:", result);
354
-
355
- try {
356
- this.config.after(data);
357
- } catch (error) {
358
- this.log("error", error.message, {
359
- after: this.config.after,
360
- });
361
- }
362
- } else
363
- this.log(
364
- "error",
365
- `Failed: ${response.status} (${response.statusText}). Details:`,
366
- result
367
- );
368
-
369
- this.logs.length = 0;
370
- })
371
- .catch(() => {
372
- if (response.ok) {
373
- this.log(
374
- "success",
375
- "Successful!",
376
- ["viewed", "basketed", "purchased"].includes(this.config.type)
377
- ? { sku: data }
378
- : data
379
- );
380
-
381
- try {
382
- this.config.after(data);
383
- } catch (error) {
384
- this.log("error", error.message, {
385
- after: this.config.after,
386
- });
387
- }
388
- } else
389
- this.log(
390
- "error",
391
- `Failed: ${response.status} (${response.statusText}). Details:`,
392
- response
393
- );
394
-
395
- this.logs.length = 0;
396
- })
397
- )
398
- .catch((error) => {
399
- this.log("error", `Failed: ${error.message}`);
400
- this.logs.length = 0;
401
- });
420
+ .catch((error) => {
421
+ this.log("error", `Failed: ${error.message}`, { payload });
422
+ this.logs.length = 0;
423
+ });
402
424
  }
403
425
  }
404
426
 
@@ -1,2 +1,2 @@
1
- /*! choreograph-create-pixel v1.4.1 2023/02/01 */
2
- var ChoreographCreatePixel=function(){"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(t){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?e(Object(i),!0).forEach((function(e){n(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,i(n.key),n)}}function n(e,t,r){return(t=i(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}var o=function(){function e(r){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.previouslyScrapedHash=null,this.logs=[],this.config=t({colors:{error:"red",warn:"orange",success:"green"},icons:{scraper:"📚",viewed:"👀",basketed:"🛒",purchased:"🧾",attribution:"🔖",conversion:"📈"},debug:/(pixel|lemonpi)_debug/i.test(location.href),before:function(e,t){return t(e)},after:function(){},optional:[]},r),this.validateConfig()&&this.cycle()}var i,o,c;return i=e,o=[{key:"log",value:function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(this.config.debug&&!this.logs.includes(t)){var i=["%c⸬ create%c ".concat(this.config.icons[this.config.type]," ").concat(this.config.type).concat(this.config.label?" (".concat(this.config.label,")"):""," %c").concat(t),"background:black;color:white;border-radius:3px;padding:3px 6px","font-weight:bold","color:".concat(this.config.colors[e])];n&&i.push(n),(r=console).info.apply(r,i),this.logs.push(t)}}},{key:"validateConfig",value:function(){if("number"!=typeof this.config.advertiser)this.log("error","Please use a number",{advertiser:this.config.advertiser});else if(["scraper","viewed","basketed","purchased","attribution","conversion"].includes(this.config.type))if(["attribution","conversion"].includes(this.config.type)&&"string"!=typeof this.config.label)this.log("error","Please use a string",{label:this.config.label});else if(this.config.url instanceof RegExp){if(["attribution","conversion"].includes(this.config.type)||this.config.scrape)return!0;this.log("error","Please provide something to scrape",{scrape:this.config.scrape})}else this.log("error","Please use a regular expression",{url:this.config.url});else this.log("error","Please use scraper, viewed, basketed, purchased, attribution or conversion",{type:this.config.type})}},{key:"cycle",value:function(){var e=this;if(this.config.url.test(location.href))if("scraper"===this.config.type&&document.querySelector('html[class*="translated-"]'))this.log("warn","This page has been translated by the browser, and won't be scraped");else if(this.config.trigger){var t="create-".concat(this.config.type,"-").concat(this.config.trigger.event);try{var r="string"==typeof this.config.trigger.elements?document.querySelectorAll(this.config.trigger.elements):this.config.trigger.elements();r.forEach||(r=[r]),r.forEach((function(r){r.hasAttribute(t)||(r.addEventListener(e.config.trigger.event,(function(){return"conversion"===e.config.type?e.convert():e.scrape(r)})),r.setAttribute(t,""))}))}catch(e){this.log("error",e.message,{"trigger.elements":this.config.trigger.elements})}}else switch(this.config.type){case"attribution":this.attribute();break;case"conversion":this.convert();break;default:this.scrape()}else this.log("warn",'URL pattern does not match "'.concat(location.href,'"'),{url:this.config.url});setTimeout((function(){return e.cycle()}),750)}},{key:"attribute",value:function(){var e=this.constructor.getQueryParameter("ccpid");if(!e)return this.log("warn","ccpid query parameter not present");if(!/^[0-9a-f]{20}$/.test(e))return this.log("error","ccpid query parameter is not formatted correctly");var t="choreograph-".concat(this.config.label);localStorage.getItem(t)!==e&&localStorage.setItem(t,e),this.log("success","Successful!",e)}},{key:"convert",value:function(){var e=localStorage.getItem("choreograph-".concat(this.config.label));if(!e)return this.log("warn",'"'.concat(this.config.label,'" attribution not present'));this.send(e)}},{key:"scrape",value:function(e){var r,i=this,o=!1,c=function(t,r){var c=t;if("function"==typeof c)try{c=c(e)}catch(e){if(i.config.optional.includes(r))return;i.log("error",e.message,n({},r?"scrape.".concat(r):"scrape",c)),o=!0}return"string"==typeof c&&(c=c.replace(/\s+/g," ").trim()),null!=c&&""!==c||i.config.optional.includes(r)||(i.log("error","Value is empty",n({},r?"scrape.".concat(r):"scrape",c)),o=!0),c};r="scraper"!==this.config.type?c(this.config.scrape):Object.keys(this.config.scrape).reduce((function(e,r){return t(t({},e),{},n({},r,c(i.config.scrape[r],r)))}),{});var s=JSON.stringify(r);if(!o&&this.previouslyScrapedHash!==s){try{this.config.before(r,(function(e){Array.isArray(e)?e.forEach((function(e){return i.send(e)})):i.send(e)}))}catch(e){this.log("error",e.message,{before:this.config.before})}this.previouslyScrapedHash=s,this.logs.length=0}}},{key:"send",value:function(e){var r,n=this;switch(this.config.type){case"scraper":r="https://d.lemonpi.io/scrapes".concat(this.config.debug?"?validate=true":"");break;case"conversion":r="https://content.lemonpi.io/track/event?e=".concat(encodeURIComponent(JSON.stringify({version:1,type:"conversion",name:this.config.label,"conversion-attribution-id":e,"advertiser-id":this.config.advertiser})));break;default:r="https://d.lemonpi.io/a/".concat(this.config.advertiser,"/product/event?e=").concat(encodeURIComponent(JSON.stringify({"event-type":"product-".concat(this.config.type),sku:e})))}"scraper"===this.config.type||this.config.debug?fetch(r,"scraper"===this.config.type?{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({"advertiser-id":this.config.advertiser,sku:e.sku,fields:t(t({},e),{},{sku:void 0})})}:null).then((function(t){return t.json().then((function(r){if(t.ok){n.log("warn","Successful, with warnings. Details:",r);try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(t.status," (").concat(t.statusText,"). Details:"),r);n.logs.length=0})).catch((function(){if(t.ok){n.log("success","Successful!",["viewed","basketed","purchased"].includes(n.config.type)?{sku:e}:e);try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(t.status," (").concat(t.statusText,"). Details:"),t);n.logs.length=0}))})).catch((function(e){n.log("error","Failed: ".concat(e.message)),n.logs.length=0})):(new Image).src=r}}],c=[{key:"getUrl",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.allowedQueryParameters,r=void 0===t?[]:t,n=e.allowHash,i=void 0!==n&&n,o="".concat(location.protocol,"//").concat(location.host).concat(location.pathname),c=new URLSearchParams(location.search);return r.reduce((function(e,t){var r=e?"&":"?",n=encodeURI(t),i=c.get(t);return null!=i?(o+="".concat(r).concat(n).concat(""===i?"":"=".concat(encodeURI(i))),!0):e}),!1),i&&(o+=location.hash),o}},{key:"getAllPathSegments",value:function(){return location.pathname.split("/").filter((function(e){return e})).map((function(e){return decodeURI(e)}))}},{key:"getPathSegment",value:function(e){return this.getAllPathSegments()[e]}},{key:"getAllQueryParameters",value:function(){return location.search.replace(/^\?/,"").split("&").filter((function(e){return e})).reduce((function(e,r){return t(t({},e),{},n({},decodeURI(r.split("=")[0]),decodeURI(r.split("=")[1]||"")))}),{})}},{key:"getQueryParameter",value:function(e){return this.getAllQueryParameters()[e]}}],o&&r(i.prototype,o),c&&r(i,c),Object.defineProperty(i,"prototype",{writable:!1}),e}();return o}();
1
+ /*! choreograph-create-pixel v1.4.3 2024/08/27 */
2
+ var ChoreographCreatePixel=function(){"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(t){for(var r=1;r<arguments.length;r++){var o=null!=arguments[r]?arguments[r]:{};r%2?e(Object(o),!0).forEach((function(e){n(t,e,o[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(o)):e(Object(o)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(o,e))}))}return t}function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,o(n.key),n)}}function n(e,t,r){return(t=o(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function o(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}var i=function(){function e(r){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.previouslyScrapedHash=null,this.logs=[],this.config=t({colors:{error:"red",warn:"orange",success:"green"},icons:{scraper:"📚",viewed:"👀",basketed:"🛒",purchased:"🧾",attribution:"🔖",conversion:"📈"},debug:/(pixel|lemonpi)_debug/i.test(location.href),before:function(e,t){return t(e)},after:function(){},optional:[]},r),this.validateConfig()&&this.cycle()}var o,i,c;return o=e,i=[{key:"log",value:function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(this.config.debug&&!this.logs.includes(t)){var o=["%c⸬ create%c ".concat(this.config.icons[this.config.type]," ").concat(this.config.type," %c").concat(t),"background:black;color:white;border-radius:3px;padding:3px 6px","font-weight:bold","color:".concat(this.config.colors[e])];n&&o.push(n),(r=console).info.apply(r,o),this.logs.push(t)}}},{key:"validateConfig",value:function(){if("number"!=typeof this.config.advertiser)this.log("error","Please use a number",{advertiser:this.config.advertiser});else if(["scraper","viewed","basketed","purchased","attribution","conversion"].includes(this.config.type))if(["attribution","conversion"].includes(this.config.type)&&"string"!=typeof this.config.label)this.log("error","Please use a string",{label:this.config.label});else if(this.config.url instanceof RegExp){if(["attribution","conversion"].includes(this.config.type)||this.config.scrape)return!0;this.log("error","Please provide something to scrape",{scrape:this.config.scrape})}else this.log("error","Please use a regular expression",{url:this.config.url});else this.log("error","Please use scraper, viewed, basketed, purchased, attribution or conversion",{type:this.config.type})}},{key:"cycle",value:function(){var e=this;if(this.config.url.test(location.href))if("scraper"===this.config.type&&document.querySelector('html[class*="translated-"]'))this.log("warn","This page has been translated by the browser, and won't be scraped");else if(this.config.trigger){var t="create-".concat(this.config.type,"-").concat(this.config.trigger.event);try{var r="string"==typeof this.config.trigger.elements?document.querySelectorAll(this.config.trigger.elements):this.config.trigger.elements();r.forEach||(r=[r]),r.forEach((function(r){r.hasAttribute(t)||(r.addEventListener(e.config.trigger.event,(function(){return"conversion"===e.config.type?e.convert():e.scrape(r)})),r.setAttribute(t,""))}))}catch(e){this.log("error",e.message,{"trigger.elements":this.config.trigger.elements})}}else switch(this.config.type){case"attribution":this.attribute();break;case"conversion":this.convert();break;default:this.scrape()}else this.log("warn",'URL pattern does not match "'.concat(location.href,'"'),{url:this.config.url});setTimeout((function(){return e.cycle()}),750)}},{key:"attribute",value:function(){var e=this.constructor.getQueryParameter("ccpid");if(!e)return this.log("warn","ccpid query parameter not present");if(!/^[0-9a-f]{20}$/.test(e))return this.log("error","ccpid query parameter is not formatted correctly");var t="choreograph-".concat(this.config.label),r=localStorage.getItem(t);if(""===r)return this.log("warn",'"'.concat(this.config.label,'" already converted'));r!==e&&localStorage.setItem(t,e),this.log("success",'Stored CCPID "'.concat(e,'" for "').concat(this.config.label,'"'))}},{key:"convert",value:function(){var e="choreograph-".concat(this.config.label),t=localStorage.getItem(e);if(!t)return this.log("warn",'"'.concat(this.config.label,'" attribution not present'));this.send(t),localStorage.setItem(e,"")}},{key:"scrape",value:function(e){var r,o=this,i=!1,c=function(t,r){var c=t;if("function"==typeof c)try{c=c(e)}catch(e){if(o.config.optional.includes(r))return;o.log("error",e.message,n({},r?"scrape.".concat(r):"scrape",c)),i=!0}return"string"==typeof c&&(c=c.replace(/\s+/g," ").trim()),null!=c&&""!==c||o.config.optional.includes(r)||(o.log("error","Value is empty",n({},r?"scrape.".concat(r):"scrape",c)),i=!0),c};r="scraper"!==this.config.type?c(this.config.scrape):Object.keys(this.config.scrape).reduce((function(e,r){return t(t({},e),{},n({},r,c(o.config.scrape[r],r)))}),{});var a=JSON.stringify(r);if(!i&&this.previouslyScrapedHash!==a){try{this.config.before(r,(function(e){Array.isArray(e)?e.forEach((function(e){return o.send(e)})):o.send(e)}))}catch(e){this.log("error",e.message,{before:this.config.before})}this.previouslyScrapedHash=a,this.logs.length=0}}},{key:"send",value:function(e){var t,r,n=this,o="GET";switch(this.config.type){case"scraper":var i=e.sku;delete e.sku,t={"advertiser-id":this.config.advertiser,sku:i,fields:e},r="https://d.lemonpi.io/scrapes".concat(this.config.debug?"?validate=true":""),o="POST";break;case"viewed":case"basketed":case"purchased":t={"event-type":"product-".concat(this.config.type),sku:e},r="https://d.lemonpi.io/a/".concat(this.config.advertiser,"/product/event?e=").concat(encodeURIComponent(JSON.stringify(t)));break;case"conversion":t={version:1,type:"conversion",name:this.config.label,"conversion-attribution-id":e,"advertiser-id":this.config.advertiser},r="https://content.lemonpi.io/track/event?e=".concat(encodeURIComponent(JSON.stringify(t)))}!["viewed","basketed","purchased"].includes(this.config.type)||this.config.debug?fetch(r,"POST"===o?{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify(t)}:null).then((function(r){return r.json().then((function(o){if(r.ok){n.log("warn","Successful, with warnings. Details:",{payload:t,result:o});try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(r.status," (").concat(r.statusText,"). Details:"),{payload:t,result:o});n.logs.length=0})).catch((function(){if(r.ok){n.log("success","Successful!",{payload:t});try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(r.status," (").concat(r.statusText,"). Details:"),{payload:t,response:r});n.logs.length=0}))})).catch((function(e){n.log("error","Failed: ".concat(e.message),{payload:t}),n.logs.length=0})):(new Image).src=r}}],c=[{key:"getUrl",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.allowedQueryParameters,r=void 0===t?[]:t,n=e.allowHash,o=void 0!==n&&n,i="".concat(location.protocol,"//").concat(location.host).concat(location.pathname),c=new URLSearchParams(location.search);return r.reduce((function(e,t){var r=e?"&":"?",n=encodeURI(t),o=c.get(t);return null!=o?(i+="".concat(r).concat(n).concat(""===o?"":"=".concat(encodeURI(o))),!0):e}),!1),o&&(i+=location.hash),i}},{key:"getAllPathSegments",value:function(){return location.pathname.split("/").filter((function(e){return e})).map((function(e){return decodeURI(e)}))}},{key:"getPathSegment",value:function(e){return this.getAllPathSegments()[e]}},{key:"getAllQueryParameters",value:function(){return location.search.replace(/^\?/,"").split("&").filter((function(e){return e})).reduce((function(e,r){return t(t({},e),{},n({},decodeURI(r.split("=")[0]),decodeURI(r.split("=")[1]||"")))}),{})}},{key:"getQueryParameter",value:function(e){return this.getAllQueryParameters()[e]}}],i&&r(o.prototype,i),c&&r(o,c),Object.defineProperty(o,"prototype",{writable:!1}),e}();return i}();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "choreograph-create-pixel",
3
3
  "description": "This library lets you apply best practises to Choreograph Create pixel development and implementation.",
4
- "version": "1.4.1",
4
+ "version": "1.4.3",
5
5
  "type": "module",
6
6
  "keywords": [
7
7
  "wpp",
@@ -11,9 +11,9 @@
11
11
  "opendc",
12
12
  "greenhouse"
13
13
  ],
14
- "author": "Rick Stevens <rick.stevens@choreograph.com> (https://lemonpi.io)",
14
+ "author": "Rick Stevens <rick.stevens@choreograph.com> (https://create.choreograph.com)",
15
15
  "repository": "gitlab:2sixty/choreograph-create/solutions/choreograph-create-pixel",
16
- "homepage": "https://lemonpi.io",
16
+ "homepage": "https://create.choreograph.com",
17
17
  "license": "ISC",
18
18
  "main": "dist/bundle.cjs.js",
19
19
  "module": "dist/bundle.esm.js",