choreograph-create-pixel 1.4.0 → 1.4.2

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.0 2023/02/01 */
1
+ /*! choreograph-create-pixel v1.4.2 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
 
@@ -203,11 +204,7 @@ class ChoreographCreatePixel {
203
204
  const ccpid = this.constructor.getQueryParameter("ccpid");
204
205
  if (!ccpid) return this.log("warn", "ccpid query parameter not present");
205
206
 
206
- if (
207
- !/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(
208
- ccpid
209
- )
210
- )
207
+ if (!/^[0-9a-f]{20}$/.test(ccpid))
211
208
  return this.log(
212
209
  "error",
213
210
  "ccpid query parameter is not formatted correctly"
@@ -216,16 +213,18 @@ class ChoreographCreatePixel {
216
213
  const storageItemLabel = `choreograph-${this.config.label}`;
217
214
  const storedCcpid = localStorage.getItem(storageItemLabel);
218
215
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
219
- this.log("success", "Successful!", ccpid);
216
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
220
217
  }
221
218
 
222
219
  convert() {
223
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
220
+ const label = `choreograph-${this.config.label}`;
221
+ const ccpid = localStorage.getItem(label);
224
222
 
225
223
  if (!ccpid)
226
224
  return this.log("warn", `"${this.config.label}" attribution not present`);
227
225
 
228
226
  this.send(ccpid);
227
+ localStorage.removeItem(label);
229
228
  }
230
229
 
231
230
  scrape(element) {
@@ -298,113 +297,128 @@ class ChoreographCreatePixel {
298
297
  }
299
298
 
300
299
  send(data) {
300
+ let payload;
301
301
  let url;
302
+ let method = "GET";
302
303
 
303
304
  switch (this.config.type) {
304
- case "scraper":
305
+ case "scraper": {
306
+ const { sku } = data;
307
+ delete data.sku;
308
+
309
+ payload = {
310
+ "advertiser-id": this.config.advertiser,
311
+ sku,
312
+ fields: data,
313
+ };
314
+
305
315
  url = `https://d.lemonpi.io/scrapes${
306
316
  this.config.debug ? "?validate=true" : ""
307
317
  }`;
308
318
 
319
+ method = "POST";
309
320
  break;
321
+ }
310
322
 
311
- case "conversion":
312
- url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
313
- JSON.stringify({
314
- version: 1,
315
- type: "conversion",
316
- name: this.config.label,
317
- "conversion-attribution-id": data,
318
- "advertiser-id": this.config.advertiser,
319
- })
320
- )}`;
321
-
322
- break;
323
+ case "viewed":
324
+ case "basketed":
325
+ case "purchased":
326
+ payload = {
327
+ "event-type": `product-${this.config.type}`,
328
+ sku: data,
329
+ };
323
330
 
324
- default:
325
331
  url = `https://d.lemonpi.io/a/${
326
332
  this.config.advertiser
327
- }/product/event?e=${encodeURIComponent(
328
- JSON.stringify({
329
- "event-type": `product-${this.config.type}`,
330
- sku: data,
331
- })
333
+ }/product/event?e=${encodeURIComponent(JSON.stringify(payload))}`;
334
+
335
+ break;
336
+
337
+ case "conversion":
338
+ payload = {
339
+ version: 1,
340
+ type: "conversion",
341
+ name: this.config.label,
342
+ "conversion-attribution-id": data,
343
+ "advertiser-id": this.config.advertiser,
344
+ };
345
+
346
+ url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
347
+ JSON.stringify(payload)
332
348
  )}`;
333
349
 
334
350
  break;
335
351
  }
336
352
 
337
- if (this.config.type !== "scraper" && !this.config.debug)
353
+ if (
354
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
355
+ !this.config.debug
356
+ ) {
338
357
  new Image().src = url;
339
- else
340
- fetch(
341
- url,
342
- this.config.type === "scraper"
343
- ? {
344
- method: "POST",
345
- headers: { "Content-Type": "application/json" },
346
- body: JSON.stringify({
347
- "advertiser-id": this.config.advertiser,
348
- sku: data.sku,
349
- fields: { ...data, sku: undefined },
350
- }),
351
- }
352
- : null
358
+ return;
359
+ }
360
+
361
+ fetch(
362
+ url,
363
+ method === "POST"
364
+ ? {
365
+ method: "POST",
366
+ headers: { "Content-Type": "application/json" },
367
+ body: JSON.stringify(payload),
368
+ }
369
+ : null
370
+ )
371
+ .then((response) =>
372
+ response
373
+ .json()
374
+ .then((result) => {
375
+ if (response.ok) {
376
+ this.log("warn", "Successful, with warnings. Details:", {
377
+ payload,
378
+ result,
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
+ { payload, result }
393
+ );
394
+
395
+ this.logs.length = 0;
396
+ })
397
+ .catch(() => {
398
+ if (response.ok) {
399
+ this.log("success", "Successful!", { payload });
400
+
401
+ try {
402
+ this.config.after(data);
403
+ } catch (error) {
404
+ this.log("error", error.message, {
405
+ after: this.config.after,
406
+ });
407
+ }
408
+ } else
409
+ this.log(
410
+ "error",
411
+ `Failed: ${response.status} (${response.statusText}). Details:`,
412
+ { payload, response }
413
+ );
414
+
415
+ this.logs.length = 0;
416
+ })
353
417
  )
354
- .then((response) =>
355
- response
356
- .json()
357
- .then((result) => {
358
- if (response.ok) {
359
- this.log("warn", "Successful, with warnings. Details:", result);
360
-
361
- try {
362
- this.config.after(data);
363
- } catch (error) {
364
- this.log("error", error.message, {
365
- after: this.config.after,
366
- });
367
- }
368
- } else
369
- this.log(
370
- "error",
371
- `Failed: ${response.status} (${response.statusText}). Details:`,
372
- result
373
- );
374
-
375
- this.logs.length = 0;
376
- })
377
- .catch(() => {
378
- if (response.ok) {
379
- this.log(
380
- "success",
381
- "Successful!",
382
- ["viewed", "basketed", "purchased"].includes(this.config.type)
383
- ? { sku: data }
384
- : data
385
- );
386
-
387
- try {
388
- this.config.after(data);
389
- } catch (error) {
390
- this.log("error", error.message, {
391
- after: this.config.after,
392
- });
393
- }
394
- } else
395
- this.log(
396
- "error",
397
- `Failed: ${response.status} (${response.statusText}). Details:`,
398
- response
399
- );
400
-
401
- this.logs.length = 0;
402
- })
403
- )
404
- .catch((error) => {
405
- this.log("error", `Failed: ${error.message}`);
406
- this.logs.length = 0;
407
- });
418
+ .catch((error) => {
419
+ this.log("error", `Failed: ${error.message}`, { payload });
420
+ this.logs.length = 0;
421
+ });
408
422
  }
409
423
  }
410
424
 
@@ -1,4 +1,4 @@
1
- /*! choreograph-create-pixel v1.4.0 2023/02/01 */
1
+ /*! choreograph-create-pixel v1.4.2 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
 
@@ -201,11 +202,7 @@ class ChoreographCreatePixel {
201
202
  const ccpid = this.constructor.getQueryParameter("ccpid");
202
203
  if (!ccpid) return this.log("warn", "ccpid query parameter not present");
203
204
 
204
- if (
205
- !/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(
206
- ccpid
207
- )
208
- )
205
+ if (!/^[0-9a-f]{20}$/.test(ccpid))
209
206
  return this.log(
210
207
  "error",
211
208
  "ccpid query parameter is not formatted correctly"
@@ -214,16 +211,18 @@ class ChoreographCreatePixel {
214
211
  const storageItemLabel = `choreograph-${this.config.label}`;
215
212
  const storedCcpid = localStorage.getItem(storageItemLabel);
216
213
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
217
- this.log("success", "Successful!", ccpid);
214
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
218
215
  }
219
216
 
220
217
  convert() {
221
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
218
+ const label = `choreograph-${this.config.label}`;
219
+ const ccpid = localStorage.getItem(label);
222
220
 
223
221
  if (!ccpid)
224
222
  return this.log("warn", `"${this.config.label}" attribution not present`);
225
223
 
226
224
  this.send(ccpid);
225
+ localStorage.removeItem(label);
227
226
  }
228
227
 
229
228
  scrape(element) {
@@ -296,113 +295,128 @@ class ChoreographCreatePixel {
296
295
  }
297
296
 
298
297
  send(data) {
298
+ let payload;
299
299
  let url;
300
+ let method = "GET";
300
301
 
301
302
  switch (this.config.type) {
302
- case "scraper":
303
+ case "scraper": {
304
+ const { sku } = data;
305
+ delete data.sku;
306
+
307
+ payload = {
308
+ "advertiser-id": this.config.advertiser,
309
+ sku,
310
+ fields: data,
311
+ };
312
+
303
313
  url = `https://d.lemonpi.io/scrapes${
304
314
  this.config.debug ? "?validate=true" : ""
305
315
  }`;
306
316
 
317
+ method = "POST";
307
318
  break;
319
+ }
308
320
 
309
- case "conversion":
310
- url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
311
- JSON.stringify({
312
- version: 1,
313
- type: "conversion",
314
- name: this.config.label,
315
- "conversion-attribution-id": data,
316
- "advertiser-id": this.config.advertiser,
317
- })
318
- )}`;
319
-
320
- break;
321
+ case "viewed":
322
+ case "basketed":
323
+ case "purchased":
324
+ payload = {
325
+ "event-type": `product-${this.config.type}`,
326
+ sku: data,
327
+ };
321
328
 
322
- default:
323
329
  url = `https://d.lemonpi.io/a/${
324
330
  this.config.advertiser
325
- }/product/event?e=${encodeURIComponent(
326
- JSON.stringify({
327
- "event-type": `product-${this.config.type}`,
328
- sku: data,
329
- })
331
+ }/product/event?e=${encodeURIComponent(JSON.stringify(payload))}`;
332
+
333
+ break;
334
+
335
+ case "conversion":
336
+ payload = {
337
+ version: 1,
338
+ type: "conversion",
339
+ name: this.config.label,
340
+ "conversion-attribution-id": data,
341
+ "advertiser-id": this.config.advertiser,
342
+ };
343
+
344
+ url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
345
+ JSON.stringify(payload)
330
346
  )}`;
331
347
 
332
348
  break;
333
349
  }
334
350
 
335
- if (this.config.type !== "scraper" && !this.config.debug)
351
+ if (
352
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
353
+ !this.config.debug
354
+ ) {
336
355
  new Image().src = url;
337
- else
338
- fetch(
339
- url,
340
- this.config.type === "scraper"
341
- ? {
342
- method: "POST",
343
- headers: { "Content-Type": "application/json" },
344
- body: JSON.stringify({
345
- "advertiser-id": this.config.advertiser,
346
- sku: data.sku,
347
- fields: { ...data, sku: undefined },
348
- }),
349
- }
350
- : null
356
+ return;
357
+ }
358
+
359
+ fetch(
360
+ url,
361
+ method === "POST"
362
+ ? {
363
+ method: "POST",
364
+ headers: { "Content-Type": "application/json" },
365
+ body: JSON.stringify(payload),
366
+ }
367
+ : null
368
+ )
369
+ .then((response) =>
370
+ response
371
+ .json()
372
+ .then((result) => {
373
+ if (response.ok) {
374
+ this.log("warn", "Successful, with warnings. Details:", {
375
+ payload,
376
+ result,
377
+ });
378
+
379
+ try {
380
+ this.config.after(data);
381
+ } catch (error) {
382
+ this.log("error", error.message, {
383
+ after: this.config.after,
384
+ });
385
+ }
386
+ } else
387
+ this.log(
388
+ "error",
389
+ `Failed: ${response.status} (${response.statusText}). Details:`,
390
+ { payload, result }
391
+ );
392
+
393
+ this.logs.length = 0;
394
+ })
395
+ .catch(() => {
396
+ if (response.ok) {
397
+ this.log("success", "Successful!", { payload });
398
+
399
+ try {
400
+ this.config.after(data);
401
+ } catch (error) {
402
+ this.log("error", error.message, {
403
+ after: this.config.after,
404
+ });
405
+ }
406
+ } else
407
+ this.log(
408
+ "error",
409
+ `Failed: ${response.status} (${response.statusText}). Details:`,
410
+ { payload, response }
411
+ );
412
+
413
+ this.logs.length = 0;
414
+ })
351
415
  )
352
- .then((response) =>
353
- response
354
- .json()
355
- .then((result) => {
356
- if (response.ok) {
357
- this.log("warn", "Successful, with warnings. Details:", result);
358
-
359
- try {
360
- this.config.after(data);
361
- } catch (error) {
362
- this.log("error", error.message, {
363
- after: this.config.after,
364
- });
365
- }
366
- } else
367
- this.log(
368
- "error",
369
- `Failed: ${response.status} (${response.statusText}). Details:`,
370
- result
371
- );
372
-
373
- this.logs.length = 0;
374
- })
375
- .catch(() => {
376
- if (response.ok) {
377
- this.log(
378
- "success",
379
- "Successful!",
380
- ["viewed", "basketed", "purchased"].includes(this.config.type)
381
- ? { sku: data }
382
- : data
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
- response
397
- );
398
-
399
- this.logs.length = 0;
400
- })
401
- )
402
- .catch((error) => {
403
- this.log("error", `Failed: ${error.message}`);
404
- this.logs.length = 0;
405
- });
416
+ .catch((error) => {
417
+ this.log("error", `Failed: ${error.message}`, { payload });
418
+ this.logs.length = 0;
419
+ });
406
420
  }
407
421
  }
408
422
 
@@ -1,2 +1,2 @@
1
- /*! choreograph-create-pixel v1.4.0 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]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.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.2 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);localStorage.getItem(t)!==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.removeItem(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.0",
4
+ "version": "1.4.2",
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",