choreograph-create-pixel 1.4.1 → 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.1 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
 
@@ -212,16 +213,18 @@ class ChoreographCreatePixel {
212
213
  const storageItemLabel = `choreograph-${this.config.label}`;
213
214
  const storedCcpid = localStorage.getItem(storageItemLabel);
214
215
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
215
- this.log("success", "Successful!", ccpid);
216
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
216
217
  }
217
218
 
218
219
  convert() {
219
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
220
+ const label = `choreograph-${this.config.label}`;
221
+ const ccpid = localStorage.getItem(label);
220
222
 
221
223
  if (!ccpid)
222
224
  return this.log("warn", `"${this.config.label}" attribution not present`);
223
225
 
224
226
  this.send(ccpid);
227
+ localStorage.removeItem(label);
225
228
  }
226
229
 
227
230
  scrape(element) {
@@ -294,113 +297,128 @@ class ChoreographCreatePixel {
294
297
  }
295
298
 
296
299
  send(data) {
300
+ let payload;
297
301
  let url;
302
+ let method = "GET";
298
303
 
299
304
  switch (this.config.type) {
300
- 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
+
301
315
  url = `https://d.lemonpi.io/scrapes${
302
316
  this.config.debug ? "?validate=true" : ""
303
317
  }`;
304
318
 
319
+ method = "POST";
305
320
  break;
321
+ }
306
322
 
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;
323
+ case "viewed":
324
+ case "basketed":
325
+ case "purchased":
326
+ payload = {
327
+ "event-type": `product-${this.config.type}`,
328
+ sku: data,
329
+ };
319
330
 
320
- default:
321
331
  url = `https://d.lemonpi.io/a/${
322
332
  this.config.advertiser
323
- }/product/event?e=${encodeURIComponent(
324
- JSON.stringify({
325
- "event-type": `product-${this.config.type}`,
326
- sku: data,
327
- })
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)
328
348
  )}`;
329
349
 
330
350
  break;
331
351
  }
332
352
 
333
- if (this.config.type !== "scraper" && !this.config.debug)
353
+ if (
354
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
355
+ !this.config.debug
356
+ ) {
334
357
  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
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
+ })
349
417
  )
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
- });
418
+ .catch((error) => {
419
+ this.log("error", `Failed: ${error.message}`, { payload });
420
+ this.logs.length = 0;
421
+ });
404
422
  }
405
423
  }
406
424
 
@@ -1,4 +1,4 @@
1
- /*! choreograph-create-pixel v1.4.1 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
 
@@ -210,16 +211,18 @@ class ChoreographCreatePixel {
210
211
  const storageItemLabel = `choreograph-${this.config.label}`;
211
212
  const storedCcpid = localStorage.getItem(storageItemLabel);
212
213
  if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
213
- this.log("success", "Successful!", ccpid);
214
+ this.log("success", `Stored CCPID "${ccpid}" for "${this.config.label}"`);
214
215
  }
215
216
 
216
217
  convert() {
217
- const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
218
+ const label = `choreograph-${this.config.label}`;
219
+ const ccpid = localStorage.getItem(label);
218
220
 
219
221
  if (!ccpid)
220
222
  return this.log("warn", `"${this.config.label}" attribution not present`);
221
223
 
222
224
  this.send(ccpid);
225
+ localStorage.removeItem(label);
223
226
  }
224
227
 
225
228
  scrape(element) {
@@ -292,113 +295,128 @@ class ChoreographCreatePixel {
292
295
  }
293
296
 
294
297
  send(data) {
298
+ let payload;
295
299
  let url;
300
+ let method = "GET";
296
301
 
297
302
  switch (this.config.type) {
298
- 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
+
299
313
  url = `https://d.lemonpi.io/scrapes${
300
314
  this.config.debug ? "?validate=true" : ""
301
315
  }`;
302
316
 
317
+ method = "POST";
303
318
  break;
319
+ }
304
320
 
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;
321
+ case "viewed":
322
+ case "basketed":
323
+ case "purchased":
324
+ payload = {
325
+ "event-type": `product-${this.config.type}`,
326
+ sku: data,
327
+ };
317
328
 
318
- default:
319
329
  url = `https://d.lemonpi.io/a/${
320
330
  this.config.advertiser
321
- }/product/event?e=${encodeURIComponent(
322
- JSON.stringify({
323
- "event-type": `product-${this.config.type}`,
324
- sku: data,
325
- })
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)
326
346
  )}`;
327
347
 
328
348
  break;
329
349
  }
330
350
 
331
- if (this.config.type !== "scraper" && !this.config.debug)
351
+ if (
352
+ ["viewed", "basketed", "purchased"].includes(this.config.type) &&
353
+ !this.config.debug
354
+ ) {
332
355
  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
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
+ })
347
415
  )
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
- });
416
+ .catch((error) => {
417
+ this.log("error", `Failed: ${error.message}`, { payload });
418
+ this.logs.length = 0;
419
+ });
402
420
  }
403
421
  }
404
422
 
@@ -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.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.1",
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",