choreograph-create-pixel 1.1.0 β 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +97 -113
- package/dist/bundle.cjs.js +149 -96
- package/dist/bundle.esm.js +149 -96
- package/dist/bundle.iife.min.js +2 -2
- package/package.json +12 -5
package/README.md
CHANGED
|
@@ -1,151 +1,135 @@
|
|
|
1
1
|
# Choreograph Create Pixel
|
|
2
2
|
|
|
3
|
-
This library lets you apply best practises to [
|
|
3
|
+
This library lets you apply best practises to [Create](https://www.lemonpi.io/) pixel development and implementation.
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- [x] Supports
|
|
7
|
+
- [x] Supports scrapers and segments
|
|
8
|
+
- [x] Supports user interaction triggers
|
|
8
9
|
- [x] Supports dynamic page changes
|
|
9
10
|
- [x] Continuous URL validation
|
|
10
|
-
- [x] Optionally trigger by user interaction
|
|
11
11
|
- [x] Prevents scraping browser-translated pages
|
|
12
12
|
- [x] Console debugger
|
|
13
13
|
|
|
14
|
-
##
|
|
14
|
+
## Pixel types
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
### Scrapers
|
|
17
17
|
|
|
18
|
-
|
|
18
|
+
Scraper pixels are used to scrape (read) data from websites, and store it as products within an advertiser's product store.
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
<script src="https://cdn.jsdelivr.net/npm/choreograph-create-pixel@1"></script>
|
|
22
|
-
<script>
|
|
23
|
-
// Scrape pixel
|
|
24
|
-
new ChoreographCreatePixel({
|
|
25
|
-
// Who is the client? (advertiser ID)
|
|
26
|
-
who: 0,
|
|
27
|
-
|
|
28
|
-
// What kind of pixel is this?
|
|
29
|
-
what: "scrape",
|
|
30
|
-
|
|
31
|
-
// Where should the pixel be enabled?
|
|
32
|
-
where: /example\.com\/products\/\d/,
|
|
33
|
-
|
|
34
|
-
// Which data should be scraped?
|
|
35
|
-
which: {
|
|
36
|
-
// Data layer example
|
|
37
|
-
sku: function () {
|
|
38
|
-
return window.dataLayer[0].product.sku;
|
|
39
|
-
},
|
|
40
|
-
|
|
41
|
-
// DOM example
|
|
42
|
-
name: function () {
|
|
43
|
-
return document.querySelector("#product-name").innerText;
|
|
44
|
-
},
|
|
45
|
-
|
|
46
|
-
// Helper example
|
|
47
|
-
url: ChoreographCreatePixel.getUrl,
|
|
48
|
-
},
|
|
49
|
-
});
|
|
20
|
+
### Segments <small>(`viewed`, `basketed` and `purchased`)</small>
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
new ChoreographCreatePixel({
|
|
53
|
-
who: 0,
|
|
54
|
-
what: "view",
|
|
55
|
-
where: /example\.com\/products\/\d/,
|
|
56
|
-
|
|
57
|
-
which: {
|
|
58
|
-
sku: function () {
|
|
59
|
-
return window.dataLayer[0].product.sku;
|
|
60
|
-
},
|
|
61
|
-
},
|
|
62
|
-
});
|
|
22
|
+
Products in ads are shown by recommendation logic. Segment pixels determine this recommendation on a consumer level, by storing a cookie at different moments during the consumer's journey. Segments can be triggered by page views, or other user interactions within a page.
|
|
63
23
|
|
|
64
|
-
|
|
65
|
-
new ChoreographCreatePixel({
|
|
66
|
-
who: 0,
|
|
67
|
-
what: "basket",
|
|
68
|
-
where: /example\.com/,
|
|
69
|
-
|
|
70
|
-
// (Optional) When should the pixel be enabled?
|
|
71
|
-
when: {
|
|
72
|
-
listener: "click",
|
|
73
|
-
|
|
74
|
-
elements: function () {
|
|
75
|
-
return document.querySelectorAll("button.basket[data-sku]");
|
|
76
|
-
},
|
|
77
|
-
},
|
|
78
|
-
|
|
79
|
-
which: {
|
|
80
|
-
// The "element" parameter only exists while using a "when" condition
|
|
81
|
-
sku: function (element) {
|
|
82
|
-
return element.getAttribute("data-sku");
|
|
83
|
-
},
|
|
84
|
-
},
|
|
85
|
-
});
|
|
24
|
+
## Quickstart
|
|
86
25
|
|
|
87
|
-
|
|
88
|
-
new ChoreographCreatePixel({
|
|
89
|
-
who: 0,
|
|
90
|
-
what: "purchase",
|
|
91
|
-
where: /example\.com\/cart/,
|
|
92
|
-
|
|
93
|
-
when: {
|
|
94
|
-
listener: "click",
|
|
95
|
-
|
|
96
|
-
elements: function () {
|
|
97
|
-
return document.querySelectorAll("button.checkout");
|
|
98
|
-
},
|
|
99
|
-
},
|
|
100
|
-
|
|
101
|
-
which: {
|
|
102
|
-
// which.sku supports returning an array of strings
|
|
103
|
-
sku: function () {
|
|
104
|
-
return window.dataLayer[0].cart.map(function (product) {
|
|
105
|
-
return product.sku;
|
|
106
|
-
});
|
|
107
|
-
},
|
|
108
|
-
},
|
|
109
|
-
});
|
|
110
|
-
</script>
|
|
111
|
-
```
|
|
26
|
+
The following snippet of all possible pixel types could be embedded on every page of _example.com_, and would automatically determine which pixel to enable where, through URL validation.
|
|
112
27
|
|
|
113
|
-
### ES module
|
|
28
|
+
### ES module
|
|
114
29
|
|
|
115
30
|
```js
|
|
116
31
|
import Pixel from "choreograph-create-pixel";
|
|
117
32
|
|
|
33
|
+
// Scraper pixel
|
|
118
34
|
new Pixel({
|
|
119
|
-
|
|
35
|
+
advertiser: 0,
|
|
36
|
+
type: "scraper",
|
|
37
|
+
|
|
38
|
+
// Where should this pixel be enabled?
|
|
39
|
+
url: /example\.com\/products\/\d/,
|
|
40
|
+
|
|
41
|
+
scrape: {
|
|
42
|
+
// Data layer example
|
|
43
|
+
sku: () => window.dataLayer[0].product.sku,
|
|
44
|
+
|
|
45
|
+
// DOM example
|
|
46
|
+
name: () => document.querySelector("#product-name").innerText,
|
|
47
|
+
|
|
48
|
+
// Helper example
|
|
49
|
+
url: Pixel.getUrl,
|
|
50
|
+
},
|
|
120
51
|
});
|
|
121
52
|
|
|
53
|
+
// Viewed segment pixel
|
|
122
54
|
new Pixel({
|
|
123
|
-
|
|
55
|
+
advertiser: 0,
|
|
56
|
+
type: "viewed",
|
|
57
|
+
url: /example\.com\/products\/\d/,
|
|
58
|
+
|
|
59
|
+
// Segment pixels only require an SKU to be scraped
|
|
60
|
+
scrape: () => window.dataLayer[0].product.sku,
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Basketed segment pixel
|
|
64
|
+
new Pixel({
|
|
65
|
+
advertiser: 0,
|
|
66
|
+
type: "basketed",
|
|
67
|
+
url: /example\.com/,
|
|
68
|
+
|
|
69
|
+
// [Optional] When should this pixel be triggered?
|
|
70
|
+
listener: {
|
|
71
|
+
event: "click",
|
|
72
|
+
elements: () => document.querySelectorAll("button.basket[data-sku]"),
|
|
73
|
+
},
|
|
74
|
+
|
|
75
|
+
// The "element" parameter can be used when using an event listener
|
|
76
|
+
scrape: (element) => element.getAttribute("data-sku"),
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
// Purchased segment pixel
|
|
80
|
+
new Pixel({
|
|
81
|
+
advertiser: 0,
|
|
82
|
+
type: "purchased",
|
|
83
|
+
url: /example\.com\/cart/,
|
|
84
|
+
|
|
85
|
+
listener: {
|
|
86
|
+
event: "click",
|
|
87
|
+
elements: () => document.querySelectorAll("button.checkout"),
|
|
88
|
+
},
|
|
89
|
+
|
|
90
|
+
// Segment pixels support multiple SKUs
|
|
91
|
+
scrape: () => window.dataLayer[0].cart.map((product) => product.sku),
|
|
124
92
|
});
|
|
125
93
|
```
|
|
126
94
|
|
|
127
95
|
> ES modules require manual bundling and transpiling before they can be safely used as browser scripts.
|
|
128
96
|
|
|
97
|
+
### CDN library
|
|
98
|
+
|
|
99
|
+
This implementation method allows for embeddeding on pages without the need for bundling or transpiling.
|
|
100
|
+
|
|
101
|
+
```html
|
|
102
|
+
<script src="https://cdn.jsdelivr.net/npm/choreograph-create-pixel@1"></script>
|
|
103
|
+
<script>
|
|
104
|
+
new ChoreographCreatePixel({
|
|
105
|
+
// ...
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
new ChoreographCreatePixel({
|
|
109
|
+
// ...
|
|
110
|
+
});
|
|
111
|
+
</script>
|
|
112
|
+
```
|
|
113
|
+
|
|
129
114
|
## Debugging
|
|
130
115
|
|
|
131
|
-
Enable
|
|
116
|
+
Enable browser console debugging by adding `?pixel_debug` or `#pixel_debug` to the page's URL.
|
|
132
117
|
|
|
133
118
|
## Configuration
|
|
134
119
|
|
|
135
|
-
| Property
|
|
136
|
-
|
|
|
137
|
-
| `
|
|
138
|
-
| `
|
|
139
|
-
| `
|
|
140
|
-
| `
|
|
141
|
-
| `
|
|
142
|
-
| `
|
|
143
|
-
| `
|
|
144
|
-
| `
|
|
145
|
-
| `
|
|
146
|
-
| `
|
|
147
|
-
| `
|
|
148
|
-
| `after` | Function | Lifecycle function that's executed just after the pixel's data is sent. | `function (data) {}` |
|
|
120
|
+
| Property | Type | Description | Default |
|
|
121
|
+
| ------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ----------------------------------------- |
|
|
122
|
+
| `advertiser` | Number | You can retrieve the advertiser ID from the URL in Create: _manage.lemonpi.io/r/AGENCY_ID/advertiser/`ADVERTISER_ID`_. | _Required_ |
|
|
123
|
+
| `type` | String | Pixel type. One of: `"scraper"`, `"viewed"`, `"basketed"` or `"purchased"`. | _Required_ |
|
|
124
|
+
| `url` | RegExp | Only enables the pixel on page URLs that are matched by this pattern. | _Required_ |
|
|
125
|
+
| `scrape`<br>_(segments only)_ | String, Array or Function | Scrapes the product's SKU for segments. | _Required_ |
|
|
126
|
+
| `scrape.*`<br>_(scrapers only)_ | String, Number, Boolean or Function | Scrapes arbitrary product data. `*` should always match with existing field names in the advertiser's product store. | _Required_ |
|
|
127
|
+
| `listener` | Object | Use an event listener when you want to enable the pixel only after a specific DOM event, instead of on page change. | `undefined` |
|
|
128
|
+
| `listener.event` | String | DOM event type. [List of supported values.](https://www.w3schools.com/jsref/dom_obj_event.asp) | _Required_ |
|
|
129
|
+
| `listener.elements` | Function | This function should return DOM element(s) to apply the event listener to. | _Required_ |
|
|
130
|
+
| `optional`<br>_(scrapers only)_ | Array | An array of field names (as used in `scrape.*`) that are allowed to have empty values. | `[]` |
|
|
131
|
+
| `before` | Function | A custom function that's executed just before the pixel is executed. | `(data, callback) => { callback(data); }` |
|
|
132
|
+
| `after` | Function | A custom function that's executed just after the pixel has been executed. | `(data) => {}` |
|
|
149
133
|
|
|
150
134
|
## Static methods (helpers)
|
|
151
135
|
|
package/dist/bundle.cjs.js
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
1
|
+
/*! choreograph-create-pixel v1.2.0 2022/10/10 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
|
+
const CONFIG = {
|
|
5
|
+
colors: { error: "red", warn: "orange", success: "green" },
|
|
6
|
+
icons: {
|
|
7
|
+
scraper: "π",
|
|
8
|
+
viewed: "π",
|
|
9
|
+
basketed: "π",
|
|
10
|
+
purchased: "π§Ύ",
|
|
11
|
+
attribution: "π",
|
|
12
|
+
conversion: "π΅",
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
4
16
|
class ChoreographCreatePixel {
|
|
5
17
|
constructor(userConfig) {
|
|
6
|
-
this.
|
|
18
|
+
this.previouslyScrapedHash = null;
|
|
7
19
|
this.logs = [];
|
|
8
20
|
|
|
9
|
-
this.settings = {
|
|
10
|
-
colors: { error: "#f44336", warn: "#ffa726", success: "#66bb6a" },
|
|
11
|
-
icons: { scrape: "π", view: "π", basket: "π", purchase: "π§Ύ" },
|
|
12
|
-
titleCss: "font:700 80% HelveticaNeue;margin:12px 0 8px",
|
|
13
|
-
messageCss: "font:500 120% HelveticaNeue;margin-bottom:12px",
|
|
14
|
-
eventTypes: {
|
|
15
|
-
view: "product-viewed",
|
|
16
|
-
basket: "product-basketed",
|
|
17
|
-
purchase: "product-purchased",
|
|
18
|
-
},
|
|
19
|
-
};
|
|
20
|
-
|
|
21
21
|
this.config = {
|
|
22
22
|
debug: /(pixel|lemonpi)_debug/i.test(location.href),
|
|
23
23
|
before: (data, callback) => callback(data),
|
|
@@ -88,14 +88,12 @@ class ChoreographCreatePixel {
|
|
|
88
88
|
if (!this.config.debug || this.logs.includes(message)) return;
|
|
89
89
|
|
|
90
90
|
const args = [
|
|
91
|
-
`%c${
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
"",
|
|
96
|
-
|
|
97
|
-
? `${this.settings.messageCss};color:${this.settings.colors[level]}`
|
|
98
|
-
: this.settings.messageCss,
|
|
91
|
+
`%cβΈ¬ create%c ${this.config.type} ${
|
|
92
|
+
CONFIG.icons[this.config.type]
|
|
93
|
+
} %c${message}`,
|
|
94
|
+
"background:black;color:white;border-radius:3px;padding:3px 6px",
|
|
95
|
+
"font-weight:bold",
|
|
96
|
+
`color:${CONFIG.colors[level]}`,
|
|
99
97
|
];
|
|
100
98
|
|
|
101
99
|
if (data) args.push(data);
|
|
@@ -104,58 +102,59 @@ class ChoreographCreatePixel {
|
|
|
104
102
|
}
|
|
105
103
|
|
|
106
104
|
validateConfig() {
|
|
107
|
-
if (typeof this.config.
|
|
108
|
-
this.log("error", "Please use a number", {
|
|
105
|
+
if (typeof this.config.advertiser !== "number")
|
|
106
|
+
this.log("error", "Please use a number", {
|
|
107
|
+
advertiser: this.config.advertiser,
|
|
108
|
+
});
|
|
109
109
|
else if (
|
|
110
|
-
!["
|
|
110
|
+
!["scraper", "viewed", "basketed", "purchased"].includes(this.config.type)
|
|
111
111
|
)
|
|
112
|
-
this.log("error", "Please use
|
|
113
|
-
|
|
112
|
+
this.log("error", "Please use scraper, viewed, basketed or purchased", {
|
|
113
|
+
type: this.config.type,
|
|
114
114
|
});
|
|
115
|
-
else if (!(this.config.
|
|
115
|
+
else if (!(this.config.url instanceof RegExp))
|
|
116
116
|
this.log("error", "Please use a regular expression", {
|
|
117
|
-
|
|
117
|
+
url: this.config.url,
|
|
118
|
+
});
|
|
119
|
+
else if (!this.config.scrape)
|
|
120
|
+
this.log("error", "Please provide something to scrape", {
|
|
121
|
+
scrape: this.config.scrape,
|
|
118
122
|
});
|
|
119
|
-
else if (typeof this.config.which !== "object" || !this.config.which.sku)
|
|
120
|
-
this.log("error", "Please provide an SKU", { which: this.config.which });
|
|
121
123
|
else return true;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
126
|
cycle() {
|
|
125
|
-
if (!this.config.
|
|
126
|
-
this.log("warn", `
|
|
127
|
-
|
|
127
|
+
if (!this.config.url.test(location.href))
|
|
128
|
+
this.log("warn", `URL pattern does not match "${location.href}"`, {
|
|
129
|
+
url: this.config.url,
|
|
128
130
|
});
|
|
129
|
-
|
|
130
|
-
this.config.
|
|
131
|
+
else if (
|
|
132
|
+
this.config.type === "scraper" &&
|
|
131
133
|
document.querySelector('html[class*="translated-"]')
|
|
132
|
-
)
|
|
134
|
+
)
|
|
133
135
|
this.log(
|
|
134
136
|
"warn",
|
|
135
|
-
"This page has been translated by the browser, and
|
|
137
|
+
"This page has been translated by the browser, and won't be scraped"
|
|
136
138
|
);
|
|
137
|
-
|
|
139
|
+
else if (this.config.listener) {
|
|
140
|
+
const attribute = `create-${this.config.type}-${this.config.listener.event}`;
|
|
141
|
+
|
|
138
142
|
try {
|
|
139
|
-
let elements = this.config.
|
|
143
|
+
let elements = this.config.listener.elements();
|
|
140
144
|
if (!elements.forEach) elements = [elements];
|
|
141
145
|
|
|
142
146
|
elements.forEach((element) => {
|
|
143
|
-
if (
|
|
144
|
-
|
|
145
|
-
) {
|
|
146
|
-
element.addEventListener(this.config.when.listener, () =>
|
|
147
|
+
if (!element.hasAttribute(attribute)) {
|
|
148
|
+
element.addEventListener(this.config.listener.event, () =>
|
|
147
149
|
this.scrape(element)
|
|
148
150
|
);
|
|
149
151
|
|
|
150
|
-
element.setAttribute(
|
|
151
|
-
`choreograph-${this.config.when.listener}`,
|
|
152
|
-
""
|
|
153
|
-
);
|
|
152
|
+
element.setAttribute(attribute, "");
|
|
154
153
|
}
|
|
155
154
|
});
|
|
156
155
|
} catch (error) {
|
|
157
156
|
this.log("error", error.message, {
|
|
158
|
-
|
|
157
|
+
"listener.elements": this.config.listener.elements,
|
|
159
158
|
});
|
|
160
159
|
}
|
|
161
160
|
} else {
|
|
@@ -166,46 +165,86 @@ class ChoreographCreatePixel {
|
|
|
166
165
|
}
|
|
167
166
|
|
|
168
167
|
scrape(element) {
|
|
169
|
-
const data = {};
|
|
170
168
|
let hasErrors = false;
|
|
169
|
+
let data;
|
|
171
170
|
|
|
172
|
-
|
|
173
|
-
|
|
171
|
+
const handleField = (field, fieldName) => {
|
|
172
|
+
let result = field;
|
|
174
173
|
|
|
175
|
-
if (typeof
|
|
174
|
+
if (typeof result === "function") {
|
|
176
175
|
try {
|
|
177
|
-
|
|
176
|
+
result = result(element);
|
|
178
177
|
} catch (error) {
|
|
179
|
-
if (
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
178
|
+
if (this.config.optional.includes(fieldName)) return null;
|
|
179
|
+
|
|
180
|
+
this.log(
|
|
181
|
+
this.config.type === "attribution" ? "warn" : "error",
|
|
182
|
+
error.message,
|
|
183
|
+
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
184
|
+
);
|
|
183
185
|
|
|
184
|
-
|
|
185
|
-
} else delete data[fieldName];
|
|
186
|
+
hasErrors = true;
|
|
186
187
|
}
|
|
187
188
|
}
|
|
188
189
|
|
|
189
|
-
if (typeof
|
|
190
|
-
|
|
190
|
+
if (typeof result === "string")
|
|
191
|
+
result = result.replace(/\s+/g, " ").trim();
|
|
191
192
|
|
|
192
|
-
if (
|
|
193
|
-
if (
|
|
194
|
-
this.log("error", "This required field's value is empty", {
|
|
195
|
-
which: { [fieldName]: data[fieldName] },
|
|
196
|
-
});
|
|
193
|
+
if (result == null || result === "") {
|
|
194
|
+
if (this.config.optional.includes(fieldName)) return null;
|
|
197
195
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
196
|
+
this.log(
|
|
197
|
+
this.config.type === "attribution" ? "warn" : "error",
|
|
198
|
+
"Value is empty",
|
|
199
|
+
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
200
|
+
);
|
|
201
|
+
|
|
202
|
+
hasErrors = true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
return result;
|
|
206
|
+
};
|
|
201
207
|
|
|
202
|
-
|
|
208
|
+
if (this.config.type === "conversion") {
|
|
209
|
+
data = {
|
|
210
|
+
conversion: handleField(this.config.scrape),
|
|
211
|
+
attribution: localStorage.getItem("create-attribution-id"),
|
|
212
|
+
};
|
|
203
213
|
|
|
204
|
-
|
|
214
|
+
if (typeof data.attribution !== "string") {
|
|
215
|
+
this.log("warn", "There's no attribution ID stored yet");
|
|
216
|
+
hasErrors = true;
|
|
217
|
+
}
|
|
218
|
+
} else if (this.config.type !== "scraper") {
|
|
219
|
+
data = handleField(this.config.scrape);
|
|
220
|
+
} else {
|
|
221
|
+
data = Object.keys(this.config.scrape).reduce(
|
|
222
|
+
(acc, fieldName) => ({
|
|
223
|
+
...acc,
|
|
224
|
+
[fieldName]: handleField(this.config.scrape[fieldName], fieldName),
|
|
225
|
+
}),
|
|
226
|
+
{}
|
|
227
|
+
);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
const dataHash = JSON.stringify(data);
|
|
231
|
+
|
|
232
|
+
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
205
233
|
try {
|
|
206
234
|
this.config.before(data, (newData) => {
|
|
207
|
-
if (this.config.
|
|
208
|
-
|
|
235
|
+
if (this.config.type === "attribution") {
|
|
236
|
+
localStorage.setItem("create-attribution-id", data);
|
|
237
|
+
this.log("success", "Successful!", { attribution: data });
|
|
238
|
+
|
|
239
|
+
try {
|
|
240
|
+
this.config.after(data);
|
|
241
|
+
} catch (error) {
|
|
242
|
+
this.log("error", error.message, {
|
|
243
|
+
after: this.config.after,
|
|
244
|
+
});
|
|
245
|
+
}
|
|
246
|
+
} else if (Array.isArray(newData))
|
|
247
|
+
newData.forEach((id) => this.send(id));
|
|
209
248
|
else this.send(newData);
|
|
210
249
|
});
|
|
211
250
|
} catch (error) {
|
|
@@ -214,42 +253,50 @@ class ChoreographCreatePixel {
|
|
|
214
253
|
});
|
|
215
254
|
}
|
|
216
255
|
|
|
217
|
-
this.
|
|
256
|
+
this.previouslyScrapedHash = dataHash;
|
|
218
257
|
this.logs.length = 0;
|
|
219
258
|
}
|
|
220
259
|
}
|
|
221
260
|
|
|
222
261
|
send(data) {
|
|
223
|
-
|
|
224
|
-
delete fields.sku;
|
|
225
|
-
|
|
226
|
-
const url =
|
|
227
|
-
this.config.what === "scrape"
|
|
228
|
-
? `https://d.lemonpi.io/scrapes${
|
|
229
|
-
this.config.debug ? "?validate=true" : ""
|
|
230
|
-
}`
|
|
231
|
-
: `https://d.lemonpi.io/a/${
|
|
232
|
-
this.config.who
|
|
233
|
-
}/product/event?e=${encodeURIComponent(
|
|
234
|
-
JSON.stringify({
|
|
235
|
-
"event-type": this.settings.eventTypes[this.config.what],
|
|
236
|
-
sku: data.sku,
|
|
237
|
-
})
|
|
238
|
-
)}`;
|
|
262
|
+
let url;
|
|
239
263
|
|
|
240
|
-
|
|
264
|
+
switch (this.config.type) {
|
|
265
|
+
case "scraper":
|
|
266
|
+
url = `https://d.lemonpi.io/scrapes${
|
|
267
|
+
this.config.debug ? "?validate=true" : ""
|
|
268
|
+
}`;
|
|
269
|
+
break;
|
|
270
|
+
|
|
271
|
+
case "conversion":
|
|
272
|
+
url = "https://lemonpi.io/"; // TO-DO
|
|
273
|
+
break;
|
|
274
|
+
|
|
275
|
+
default:
|
|
276
|
+
url = `https://d.lemonpi.io/a/${
|
|
277
|
+
this.config.advertiser
|
|
278
|
+
}/product/event?e=${encodeURIComponent(
|
|
279
|
+
JSON.stringify({
|
|
280
|
+
"event-type": `product-${this.config.type}`,
|
|
281
|
+
sku: data,
|
|
282
|
+
})
|
|
283
|
+
)}`;
|
|
284
|
+
break;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (this.config.type !== "scraper" && !this.config.debug)
|
|
241
288
|
new Image().src = url;
|
|
242
289
|
else
|
|
243
290
|
fetch(
|
|
244
291
|
url,
|
|
245
|
-
this.config.
|
|
292
|
+
this.config.type === "scraper"
|
|
246
293
|
? {
|
|
247
294
|
method: "POST",
|
|
248
295
|
headers: { "Content-Type": "application/json" },
|
|
249
296
|
body: JSON.stringify({
|
|
250
|
-
"advertiser-id": this.config.
|
|
297
|
+
"advertiser-id": this.config.advertiser,
|
|
251
298
|
sku: data.sku,
|
|
252
|
-
fields,
|
|
299
|
+
fields: { ...data, sku: undefined },
|
|
253
300
|
}),
|
|
254
301
|
}
|
|
255
302
|
: null
|
|
@@ -279,7 +326,13 @@ class ChoreographCreatePixel {
|
|
|
279
326
|
})
|
|
280
327
|
.catch(() => {
|
|
281
328
|
if (response.ok) {
|
|
282
|
-
this.log(
|
|
329
|
+
this.log(
|
|
330
|
+
"success",
|
|
331
|
+
"Successful!",
|
|
332
|
+
["viewed", "basketed", "purchased"].includes(this.config.type)
|
|
333
|
+
? { sku: data }
|
|
334
|
+
: data
|
|
335
|
+
);
|
|
283
336
|
|
|
284
337
|
try {
|
|
285
338
|
this.config.after(data);
|
package/dist/bundle.esm.js
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
1
|
+
/*! choreograph-create-pixel v1.2.0 2022/10/10 */
|
|
2
|
+
const CONFIG = {
|
|
3
|
+
colors: { error: "red", warn: "orange", success: "green" },
|
|
4
|
+
icons: {
|
|
5
|
+
scraper: "π",
|
|
6
|
+
viewed: "π",
|
|
7
|
+
basketed: "π",
|
|
8
|
+
purchased: "π§Ύ",
|
|
9
|
+
attribution: "π",
|
|
10
|
+
conversion: "π΅",
|
|
11
|
+
},
|
|
12
|
+
};
|
|
13
|
+
|
|
2
14
|
class ChoreographCreatePixel {
|
|
3
15
|
constructor(userConfig) {
|
|
4
|
-
this.
|
|
16
|
+
this.previouslyScrapedHash = null;
|
|
5
17
|
this.logs = [];
|
|
6
18
|
|
|
7
|
-
this.settings = {
|
|
8
|
-
colors: { error: "#f44336", warn: "#ffa726", success: "#66bb6a" },
|
|
9
|
-
icons: { scrape: "π", view: "π", basket: "π", purchase: "π§Ύ" },
|
|
10
|
-
titleCss: "font:700 80% HelveticaNeue;margin:12px 0 8px",
|
|
11
|
-
messageCss: "font:500 120% HelveticaNeue;margin-bottom:12px",
|
|
12
|
-
eventTypes: {
|
|
13
|
-
view: "product-viewed",
|
|
14
|
-
basket: "product-basketed",
|
|
15
|
-
purchase: "product-purchased",
|
|
16
|
-
},
|
|
17
|
-
};
|
|
18
|
-
|
|
19
19
|
this.config = {
|
|
20
20
|
debug: /(pixel|lemonpi)_debug/i.test(location.href),
|
|
21
21
|
before: (data, callback) => callback(data),
|
|
@@ -86,14 +86,12 @@ class ChoreographCreatePixel {
|
|
|
86
86
|
if (!this.config.debug || this.logs.includes(message)) return;
|
|
87
87
|
|
|
88
88
|
const args = [
|
|
89
|
-
`%c${
|
|
90
|
-
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
"",
|
|
94
|
-
|
|
95
|
-
? `${this.settings.messageCss};color:${this.settings.colors[level]}`
|
|
96
|
-
: this.settings.messageCss,
|
|
89
|
+
`%cβΈ¬ create%c ${this.config.type} ${
|
|
90
|
+
CONFIG.icons[this.config.type]
|
|
91
|
+
} %c${message}`,
|
|
92
|
+
"background:black;color:white;border-radius:3px;padding:3px 6px",
|
|
93
|
+
"font-weight:bold",
|
|
94
|
+
`color:${CONFIG.colors[level]}`,
|
|
97
95
|
];
|
|
98
96
|
|
|
99
97
|
if (data) args.push(data);
|
|
@@ -102,58 +100,59 @@ class ChoreographCreatePixel {
|
|
|
102
100
|
}
|
|
103
101
|
|
|
104
102
|
validateConfig() {
|
|
105
|
-
if (typeof this.config.
|
|
106
|
-
this.log("error", "Please use a number", {
|
|
103
|
+
if (typeof this.config.advertiser !== "number")
|
|
104
|
+
this.log("error", "Please use a number", {
|
|
105
|
+
advertiser: this.config.advertiser,
|
|
106
|
+
});
|
|
107
107
|
else if (
|
|
108
|
-
!["
|
|
108
|
+
!["scraper", "viewed", "basketed", "purchased"].includes(this.config.type)
|
|
109
109
|
)
|
|
110
|
-
this.log("error", "Please use
|
|
111
|
-
|
|
110
|
+
this.log("error", "Please use scraper, viewed, basketed or purchased", {
|
|
111
|
+
type: this.config.type,
|
|
112
112
|
});
|
|
113
|
-
else if (!(this.config.
|
|
113
|
+
else if (!(this.config.url instanceof RegExp))
|
|
114
114
|
this.log("error", "Please use a regular expression", {
|
|
115
|
-
|
|
115
|
+
url: this.config.url,
|
|
116
|
+
});
|
|
117
|
+
else if (!this.config.scrape)
|
|
118
|
+
this.log("error", "Please provide something to scrape", {
|
|
119
|
+
scrape: this.config.scrape,
|
|
116
120
|
});
|
|
117
|
-
else if (typeof this.config.which !== "object" || !this.config.which.sku)
|
|
118
|
-
this.log("error", "Please provide an SKU", { which: this.config.which });
|
|
119
121
|
else return true;
|
|
120
122
|
}
|
|
121
123
|
|
|
122
124
|
cycle() {
|
|
123
|
-
if (!this.config.
|
|
124
|
-
this.log("warn", `
|
|
125
|
-
|
|
125
|
+
if (!this.config.url.test(location.href))
|
|
126
|
+
this.log("warn", `URL pattern does not match "${location.href}"`, {
|
|
127
|
+
url: this.config.url,
|
|
126
128
|
});
|
|
127
|
-
|
|
128
|
-
this.config.
|
|
129
|
+
else if (
|
|
130
|
+
this.config.type === "scraper" &&
|
|
129
131
|
document.querySelector('html[class*="translated-"]')
|
|
130
|
-
)
|
|
132
|
+
)
|
|
131
133
|
this.log(
|
|
132
134
|
"warn",
|
|
133
|
-
"This page has been translated by the browser, and
|
|
135
|
+
"This page has been translated by the browser, and won't be scraped"
|
|
134
136
|
);
|
|
135
|
-
|
|
137
|
+
else if (this.config.listener) {
|
|
138
|
+
const attribute = `create-${this.config.type}-${this.config.listener.event}`;
|
|
139
|
+
|
|
136
140
|
try {
|
|
137
|
-
let elements = this.config.
|
|
141
|
+
let elements = this.config.listener.elements();
|
|
138
142
|
if (!elements.forEach) elements = [elements];
|
|
139
143
|
|
|
140
144
|
elements.forEach((element) => {
|
|
141
|
-
if (
|
|
142
|
-
|
|
143
|
-
) {
|
|
144
|
-
element.addEventListener(this.config.when.listener, () =>
|
|
145
|
+
if (!element.hasAttribute(attribute)) {
|
|
146
|
+
element.addEventListener(this.config.listener.event, () =>
|
|
145
147
|
this.scrape(element)
|
|
146
148
|
);
|
|
147
149
|
|
|
148
|
-
element.setAttribute(
|
|
149
|
-
`choreograph-${this.config.when.listener}`,
|
|
150
|
-
""
|
|
151
|
-
);
|
|
150
|
+
element.setAttribute(attribute, "");
|
|
152
151
|
}
|
|
153
152
|
});
|
|
154
153
|
} catch (error) {
|
|
155
154
|
this.log("error", error.message, {
|
|
156
|
-
|
|
155
|
+
"listener.elements": this.config.listener.elements,
|
|
157
156
|
});
|
|
158
157
|
}
|
|
159
158
|
} else {
|
|
@@ -164,46 +163,86 @@ class ChoreographCreatePixel {
|
|
|
164
163
|
}
|
|
165
164
|
|
|
166
165
|
scrape(element) {
|
|
167
|
-
const data = {};
|
|
168
166
|
let hasErrors = false;
|
|
167
|
+
let data;
|
|
169
168
|
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
const handleField = (field, fieldName) => {
|
|
170
|
+
let result = field;
|
|
172
171
|
|
|
173
|
-
if (typeof
|
|
172
|
+
if (typeof result === "function") {
|
|
174
173
|
try {
|
|
175
|
-
|
|
174
|
+
result = result(element);
|
|
176
175
|
} catch (error) {
|
|
177
|
-
if (
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
176
|
+
if (this.config.optional.includes(fieldName)) return null;
|
|
177
|
+
|
|
178
|
+
this.log(
|
|
179
|
+
this.config.type === "attribution" ? "warn" : "error",
|
|
180
|
+
error.message,
|
|
181
|
+
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
182
|
+
);
|
|
181
183
|
|
|
182
|
-
|
|
183
|
-
} else delete data[fieldName];
|
|
184
|
+
hasErrors = true;
|
|
184
185
|
}
|
|
185
186
|
}
|
|
186
187
|
|
|
187
|
-
if (typeof
|
|
188
|
-
|
|
188
|
+
if (typeof result === "string")
|
|
189
|
+
result = result.replace(/\s+/g, " ").trim();
|
|
189
190
|
|
|
190
|
-
if (
|
|
191
|
-
if (
|
|
192
|
-
this.log("error", "This required field's value is empty", {
|
|
193
|
-
which: { [fieldName]: data[fieldName] },
|
|
194
|
-
});
|
|
191
|
+
if (result == null || result === "") {
|
|
192
|
+
if (this.config.optional.includes(fieldName)) return null;
|
|
195
193
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
194
|
+
this.log(
|
|
195
|
+
this.config.type === "attribution" ? "warn" : "error",
|
|
196
|
+
"Value is empty",
|
|
197
|
+
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
198
|
+
);
|
|
199
|
+
|
|
200
|
+
hasErrors = true;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return result;
|
|
204
|
+
};
|
|
199
205
|
|
|
200
|
-
|
|
206
|
+
if (this.config.type === "conversion") {
|
|
207
|
+
data = {
|
|
208
|
+
conversion: handleField(this.config.scrape),
|
|
209
|
+
attribution: localStorage.getItem("create-attribution-id"),
|
|
210
|
+
};
|
|
201
211
|
|
|
202
|
-
|
|
212
|
+
if (typeof data.attribution !== "string") {
|
|
213
|
+
this.log("warn", "There's no attribution ID stored yet");
|
|
214
|
+
hasErrors = true;
|
|
215
|
+
}
|
|
216
|
+
} else if (this.config.type !== "scraper") {
|
|
217
|
+
data = handleField(this.config.scrape);
|
|
218
|
+
} else {
|
|
219
|
+
data = Object.keys(this.config.scrape).reduce(
|
|
220
|
+
(acc, fieldName) => ({
|
|
221
|
+
...acc,
|
|
222
|
+
[fieldName]: handleField(this.config.scrape[fieldName], fieldName),
|
|
223
|
+
}),
|
|
224
|
+
{}
|
|
225
|
+
);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
const dataHash = JSON.stringify(data);
|
|
229
|
+
|
|
230
|
+
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
203
231
|
try {
|
|
204
232
|
this.config.before(data, (newData) => {
|
|
205
|
-
if (this.config.
|
|
206
|
-
|
|
233
|
+
if (this.config.type === "attribution") {
|
|
234
|
+
localStorage.setItem("create-attribution-id", data);
|
|
235
|
+
this.log("success", "Successful!", { attribution: data });
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
this.config.after(data);
|
|
239
|
+
} catch (error) {
|
|
240
|
+
this.log("error", error.message, {
|
|
241
|
+
after: this.config.after,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
} else if (Array.isArray(newData))
|
|
245
|
+
newData.forEach((id) => this.send(id));
|
|
207
246
|
else this.send(newData);
|
|
208
247
|
});
|
|
209
248
|
} catch (error) {
|
|
@@ -212,42 +251,50 @@ class ChoreographCreatePixel {
|
|
|
212
251
|
});
|
|
213
252
|
}
|
|
214
253
|
|
|
215
|
-
this.
|
|
254
|
+
this.previouslyScrapedHash = dataHash;
|
|
216
255
|
this.logs.length = 0;
|
|
217
256
|
}
|
|
218
257
|
}
|
|
219
258
|
|
|
220
259
|
send(data) {
|
|
221
|
-
|
|
222
|
-
delete fields.sku;
|
|
223
|
-
|
|
224
|
-
const url =
|
|
225
|
-
this.config.what === "scrape"
|
|
226
|
-
? `https://d.lemonpi.io/scrapes${
|
|
227
|
-
this.config.debug ? "?validate=true" : ""
|
|
228
|
-
}`
|
|
229
|
-
: `https://d.lemonpi.io/a/${
|
|
230
|
-
this.config.who
|
|
231
|
-
}/product/event?e=${encodeURIComponent(
|
|
232
|
-
JSON.stringify({
|
|
233
|
-
"event-type": this.settings.eventTypes[this.config.what],
|
|
234
|
-
sku: data.sku,
|
|
235
|
-
})
|
|
236
|
-
)}`;
|
|
260
|
+
let url;
|
|
237
261
|
|
|
238
|
-
|
|
262
|
+
switch (this.config.type) {
|
|
263
|
+
case "scraper":
|
|
264
|
+
url = `https://d.lemonpi.io/scrapes${
|
|
265
|
+
this.config.debug ? "?validate=true" : ""
|
|
266
|
+
}`;
|
|
267
|
+
break;
|
|
268
|
+
|
|
269
|
+
case "conversion":
|
|
270
|
+
url = "https://lemonpi.io/"; // TO-DO
|
|
271
|
+
break;
|
|
272
|
+
|
|
273
|
+
default:
|
|
274
|
+
url = `https://d.lemonpi.io/a/${
|
|
275
|
+
this.config.advertiser
|
|
276
|
+
}/product/event?e=${encodeURIComponent(
|
|
277
|
+
JSON.stringify({
|
|
278
|
+
"event-type": `product-${this.config.type}`,
|
|
279
|
+
sku: data,
|
|
280
|
+
})
|
|
281
|
+
)}`;
|
|
282
|
+
break;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
if (this.config.type !== "scraper" && !this.config.debug)
|
|
239
286
|
new Image().src = url;
|
|
240
287
|
else
|
|
241
288
|
fetch(
|
|
242
289
|
url,
|
|
243
|
-
this.config.
|
|
290
|
+
this.config.type === "scraper"
|
|
244
291
|
? {
|
|
245
292
|
method: "POST",
|
|
246
293
|
headers: { "Content-Type": "application/json" },
|
|
247
294
|
body: JSON.stringify({
|
|
248
|
-
"advertiser-id": this.config.
|
|
295
|
+
"advertiser-id": this.config.advertiser,
|
|
249
296
|
sku: data.sku,
|
|
250
|
-
fields,
|
|
297
|
+
fields: { ...data, sku: undefined },
|
|
251
298
|
}),
|
|
252
299
|
}
|
|
253
300
|
: null
|
|
@@ -277,7 +324,13 @@ class ChoreographCreatePixel {
|
|
|
277
324
|
})
|
|
278
325
|
.catch(() => {
|
|
279
326
|
if (response.ok) {
|
|
280
|
-
this.log(
|
|
327
|
+
this.log(
|
|
328
|
+
"success",
|
|
329
|
+
"Successful!",
|
|
330
|
+
["viewed", "basketed", "purchased"].includes(this.config.type)
|
|
331
|
+
? { sku: data }
|
|
332
|
+
: data
|
|
333
|
+
);
|
|
281
334
|
|
|
282
335
|
try {
|
|
283
336
|
this.config.after(data);
|
package/dist/bundle.iife.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
2
|
-
var ChoreographCreatePixel=function(){"use strict";function e(e,t){var
|
|
1
|
+
/*! choreograph-create-pixel v1.2.0 2022/10/10 */
|
|
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,n.key,n)}}function n(e,t,r){return t in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}var o={colors:{error:"red",warn:"orange",success:"green"},icons:{scraper:"π",viewed:"π",basketed:"π",purchased:"π§Ύ",attribution:"π",conversion:"π΅"}},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({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,c,s;return i=e,c=[{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.type," ").concat(o.icons[this.config.type]," %c").concat(t),"background:black;color:white;border-radius:3px;padding:3px 6px","font-weight:bold","color:".concat(o.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"].includes(this.config.type))if(this.config.url instanceof RegExp){if(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 or purchased",{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.listener){var t="create-".concat(this.config.type,"-").concat(this.config.listener.event);try{var r=this.config.listener.elements();r.forEach||(r=[r]),r.forEach((function(r){r.hasAttribute(t)||(r.addEventListener(e.config.listener.event,(function(){return e.scrape(r)})),r.setAttribute(t,""))}))}catch(e){this.log("error",e.message,{"listener.elements":this.config.listener.elements})}}else 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:"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 null;o.log("attribution"===o.config.type?"warn":"error",e.message,n({},r?"scrape.".concat(r):"scrape",c)),i=!0}if("string"==typeof c&&(c=c.replace(/\s+/g," ").trim()),null==c||""===c){if(o.config.optional.includes(r))return null;o.log("attribution"===o.config.type?"warn":"error","Value is empty",n({},r?"scrape.".concat(r):"scrape",c)),i=!0}return c};"conversion"===this.config.type?"string"!=typeof(r={conversion:c(this.config.scrape),attribution:localStorage.getItem("create-attribution-id")}).attribution&&(this.log("warn","There's no attribution ID stored yet"),i=!0):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 s=JSON.stringify(r);if(!i&&this.previouslyScrapedHash!==s){try{this.config.before(r,(function(e){if("attribution"===o.config.type){localStorage.setItem("create-attribution-id",r),o.log("success","Successful!",{attribution:r});try{o.config.after(r)}catch(e){o.log("error",e.message,{after:o.config.after})}}else 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=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://lemonpi.io/";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}}],s=[{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]}}],c&&r(i.prototype,c),s&&r(i,s),Object.defineProperty(i,"prototype",{writable:!1}),e}();return i}();
|
package/package.json
CHANGED
|
@@ -1,7 +1,14 @@
|
|
|
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
|
-
"
|
|
4
|
+
"keywords": [
|
|
5
|
+
"wpp",
|
|
6
|
+
"groupm",
|
|
7
|
+
"lemonpi",
|
|
8
|
+
"odc",
|
|
9
|
+
"opendc"
|
|
10
|
+
],
|
|
11
|
+
"version": "1.2.0",
|
|
5
12
|
"author": "Rick Stevens <rick.stevens@choreograph.com> (https://lemonpi.io)",
|
|
6
13
|
"repository": "gitlab:GreenhouseGroup/lemonpi/solutions/choreograph-create-pixel",
|
|
7
14
|
"homepage": "https://lemonpi.io",
|
|
@@ -18,14 +25,14 @@
|
|
|
18
25
|
"format": "prettier --ignore-path .gitignore --check . '!**/*.{js,jsx,vue}'",
|
|
19
26
|
"build": "rollup -c",
|
|
20
27
|
"dev": "rollup -cw",
|
|
21
|
-
"
|
|
28
|
+
"prepublishOnly": "npm run build"
|
|
22
29
|
},
|
|
23
30
|
"devDependencies": {
|
|
24
|
-
"@babel/core": "^7.19.
|
|
25
|
-
"@babel/preset-env": "^7.19.
|
|
31
|
+
"@babel/core": "^7.19.3",
|
|
32
|
+
"@babel/preset-env": "^7.19.3",
|
|
26
33
|
"@rollup/plugin-babel": "^5.3.1",
|
|
27
34
|
"@rollup/plugin-eslint": "^8.0.2",
|
|
28
|
-
"eslint": "^8.
|
|
35
|
+
"eslint": "^8.24.0",
|
|
29
36
|
"eslint-config-prettier": "^8.5.0",
|
|
30
37
|
"eslint-plugin-prettier": "^4.2.1",
|
|
31
38
|
"moment": "^2.29.4",
|