choreograph-create-pixel 1.3.3 β 1.4.1
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 +35 -5
- package/dist/bundle.cjs.js +92 -48
- package/dist/bundle.esm.js +92 -48
- package/dist/bundle.iife.min.js +2 -2
- package/package.json +13 -9
package/README.md
CHANGED
|
@@ -4,7 +4,7 @@ This library lets you apply best practises to [Create](https://www.lemonpi.io/)
|
|
|
4
4
|
|
|
5
5
|
## Features
|
|
6
6
|
|
|
7
|
-
- [x] Supports [scraper](#scraper-pixels), [segment](#segment-pixels) and [
|
|
7
|
+
- [x] Supports [scraper](#scraper-pixels), [segment](#segment-pixels) and [post-click](#post-click-pixels) pixels
|
|
8
8
|
- [x] Supports dynamic page content updates
|
|
9
9
|
- [x] Prevents scraping browser-translated content
|
|
10
10
|
- [x] Continuous URL validation
|
|
@@ -15,7 +15,7 @@ This library lets you apply best practises to [Create](https://www.lemonpi.io/)
|
|
|
15
15
|
|
|
16
16
|
<small>Type: `scraper`</small>
|
|
17
17
|
|
|
18
|
-
Scraper pixels are used to scrape (
|
|
18
|
+
Scraper pixels are 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
|
|
|
@@ -23,9 +23,11 @@ Scraper pixels are used to scrape (read) data from websites, and store that data
|
|
|
23
23
|
|
|
24
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.
|
|
25
25
|
|
|
26
|
-
###
|
|
26
|
+
### Post-click pixels
|
|
27
27
|
|
|
28
|
-
|
|
28
|
+
<small>Types: `attribution` and `conversion`</small>
|
|
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.
|
|
29
31
|
|
|
30
32
|
## Quickstart
|
|
31
33
|
|
|
@@ -44,7 +46,7 @@ new Pixel({
|
|
|
44
46
|
// Where on the website should this pixel be enabled?
|
|
45
47
|
url: /example\.com\/products\/\d/,
|
|
46
48
|
|
|
47
|
-
//
|
|
49
|
+
// Scrape functions are continuously re-evaluated for value changes
|
|
48
50
|
scrape: {
|
|
49
51
|
// Data layer example
|
|
50
52
|
sku: () => window.dataLayer[0].product.sku,
|
|
@@ -97,6 +99,33 @@ new Pixel({
|
|
|
97
99
|
// Segment pixels support multiple SKUs
|
|
98
100
|
scrape: () => window.dataLayer[0].cart.map((product) => product.sku),
|
|
99
101
|
});
|
|
102
|
+
|
|
103
|
+
// Attribution post-click pixel
|
|
104
|
+
new Pixel({
|
|
105
|
+
advertiser: 0,
|
|
106
|
+
type: "attribution",
|
|
107
|
+
|
|
108
|
+
// Uniquely label each set of post-click pixels
|
|
109
|
+
label: "example",
|
|
110
|
+
|
|
111
|
+
// Match this URL to the landing page of your campaign
|
|
112
|
+
url: /example\.com\/products\/\d/,
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// Conversion post-click pixel
|
|
116
|
+
new Pixel({
|
|
117
|
+
advertiser: 0,
|
|
118
|
+
type: "conversion",
|
|
119
|
+
label: "example",
|
|
120
|
+
|
|
121
|
+
// Match this URL to the conversion page of your campaign
|
|
122
|
+
url: /example\.com\/cart/,
|
|
123
|
+
|
|
124
|
+
trigger: {
|
|
125
|
+
event: "click",
|
|
126
|
+
elements: "button.checkout",
|
|
127
|
+
},
|
|
128
|
+
});
|
|
100
129
|
```
|
|
101
130
|
|
|
102
131
|
> ES modules require manual bundling and transpiling before they can be safely embedded on websites.
|
|
@@ -128,6 +157,7 @@ Enable browser console debugging by adding `?pixel_debug` or `#pixel_debug` to t
|
|
|
128
157
|
| ------------------------------- | ----------------------------------- | ---------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |
|
|
129
158
|
| `advertiser` | Number | You can retrieve the advertiser ID from the URL in Create: _manage.lemonpi.io/r/AGENCY_ID/advertiser/`ADVERTISER_ID`_. | _Required_ |
|
|
130
159
|
| `type` | String | Pixel type. One of: `"scraper"`, `"viewed"`, `"basketed"` or `"purchased"`. | _Required_ |
|
|
160
|
+
| `label`<br>_(post-click only)_ | String | Conversion label, used for aggregation in reporting. | _Required_ |
|
|
131
161
|
| `url` | RegExp | Only enables the pixel on page URLs that are matched by this pattern. | _Required_ |
|
|
132
162
|
| `scrape`<br>_(segments only)_ | String, Array or Function | Scrapes the product's SKU for segments. | _Required_ |
|
|
133
163
|
| `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_ |
|
package/dist/bundle.cjs.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
1
|
+
/*! choreograph-create-pixel v1.4.1 2023/02/01 */
|
|
2
2
|
'use strict';
|
|
3
3
|
|
|
4
4
|
class ChoreographCreatePixel {
|
|
@@ -14,7 +14,7 @@ class ChoreographCreatePixel {
|
|
|
14
14
|
basketed: "π",
|
|
15
15
|
purchased: "π§Ύ",
|
|
16
16
|
attribution: "π",
|
|
17
|
-
conversion: "
|
|
17
|
+
conversion: "π",
|
|
18
18
|
},
|
|
19
19
|
debug: /(pixel|lemonpi)_debug/i.test(location.href),
|
|
20
20
|
before: (data, callback) => callback(data),
|
|
@@ -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.type} ${
|
|
89
|
-
this.config.
|
|
88
|
+
`%cβΈ¬ create%c ${this.config.icons[this.config.type]} ${this.config.type}${
|
|
89
|
+
this.config.label ? ` (${this.config.label})` : ""
|
|
90
90
|
} %c${message}`,
|
|
91
91
|
"background:black;color:white;border-radius:3px;padding:3px 6px",
|
|
92
92
|
"font-weight:bold",
|
|
@@ -104,16 +104,35 @@ class ChoreographCreatePixel {
|
|
|
104
104
|
advertiser: this.config.advertiser,
|
|
105
105
|
});
|
|
106
106
|
else if (
|
|
107
|
-
![
|
|
107
|
+
![
|
|
108
|
+
"scraper",
|
|
109
|
+
"viewed",
|
|
110
|
+
"basketed",
|
|
111
|
+
"purchased",
|
|
112
|
+
"attribution",
|
|
113
|
+
"conversion",
|
|
114
|
+
].includes(this.config.type)
|
|
108
115
|
)
|
|
109
|
-
this.log(
|
|
110
|
-
|
|
116
|
+
this.log(
|
|
117
|
+
"error",
|
|
118
|
+
"Please use scraper, viewed, basketed, purchased, attribution or conversion",
|
|
119
|
+
{ type: this.config.type }
|
|
120
|
+
);
|
|
121
|
+
else if (
|
|
122
|
+
["attribution", "conversion"].includes(this.config.type) &&
|
|
123
|
+
typeof this.config.label !== "string"
|
|
124
|
+
)
|
|
125
|
+
this.log("error", "Please use a string", {
|
|
126
|
+
label: this.config.label,
|
|
111
127
|
});
|
|
112
128
|
else if (!(this.config.url instanceof RegExp))
|
|
113
129
|
this.log("error", "Please use a regular expression", {
|
|
114
130
|
url: this.config.url,
|
|
115
131
|
});
|
|
116
|
-
else if (
|
|
132
|
+
else if (
|
|
133
|
+
!["attribution", "conversion"].includes(this.config.type) &&
|
|
134
|
+
!this.config.scrape
|
|
135
|
+
)
|
|
117
136
|
this.log("error", "Please provide something to scrape", {
|
|
118
137
|
scrape: this.config.scrape,
|
|
119
138
|
});
|
|
@@ -134,7 +153,8 @@ class ChoreographCreatePixel {
|
|
|
134
153
|
"This page has been translated by the browser, and won't be scraped"
|
|
135
154
|
);
|
|
136
155
|
else if (this.config.trigger) {
|
|
137
|
-
|
|
156
|
+
// TO-DO: replace this.config.type with a unique pixel instance ID
|
|
157
|
+
const elementAttribute = `create-${this.config.type}-${this.config.trigger.event}`;
|
|
138
158
|
|
|
139
159
|
try {
|
|
140
160
|
let elements =
|
|
@@ -145,12 +165,14 @@ class ChoreographCreatePixel {
|
|
|
145
165
|
if (!elements.forEach) elements = [elements];
|
|
146
166
|
|
|
147
167
|
elements.forEach((element) => {
|
|
148
|
-
if (!element.hasAttribute(
|
|
168
|
+
if (!element.hasAttribute(elementAttribute)) {
|
|
149
169
|
element.addEventListener(this.config.trigger.event, () =>
|
|
150
|
-
this.
|
|
170
|
+
this.config.type === "conversion"
|
|
171
|
+
? this.convert()
|
|
172
|
+
: this.scrape(element)
|
|
151
173
|
);
|
|
152
174
|
|
|
153
|
-
element.setAttribute(
|
|
175
|
+
element.setAttribute(elementAttribute, "");
|
|
154
176
|
}
|
|
155
177
|
});
|
|
156
178
|
} catch (error) {
|
|
@@ -159,12 +181,49 @@ class ChoreographCreatePixel {
|
|
|
159
181
|
});
|
|
160
182
|
}
|
|
161
183
|
} else {
|
|
162
|
-
this.
|
|
184
|
+
switch (this.config.type) {
|
|
185
|
+
case "attribution":
|
|
186
|
+
this.attribute();
|
|
187
|
+
break;
|
|
188
|
+
|
|
189
|
+
case "conversion":
|
|
190
|
+
this.convert();
|
|
191
|
+
break;
|
|
192
|
+
|
|
193
|
+
default:
|
|
194
|
+
this.scrape();
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
163
197
|
}
|
|
164
198
|
|
|
165
199
|
setTimeout(() => this.cycle(), 750);
|
|
166
200
|
}
|
|
167
201
|
|
|
202
|
+
attribute() {
|
|
203
|
+
const ccpid = this.constructor.getQueryParameter("ccpid");
|
|
204
|
+
if (!ccpid) return this.log("warn", "ccpid query parameter not present");
|
|
205
|
+
|
|
206
|
+
if (!/^[0-9a-f]{20}$/.test(ccpid))
|
|
207
|
+
return this.log(
|
|
208
|
+
"error",
|
|
209
|
+
"ccpid query parameter is not formatted correctly"
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
const storageItemLabel = `choreograph-${this.config.label}`;
|
|
213
|
+
const storedCcpid = localStorage.getItem(storageItemLabel);
|
|
214
|
+
if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
|
|
215
|
+
this.log("success", "Successful!", ccpid);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
convert() {
|
|
219
|
+
const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
|
|
220
|
+
|
|
221
|
+
if (!ccpid)
|
|
222
|
+
return this.log("warn", `"${this.config.label}" attribution not present`);
|
|
223
|
+
|
|
224
|
+
this.send(ccpid);
|
|
225
|
+
}
|
|
226
|
+
|
|
168
227
|
scrape(element) {
|
|
169
228
|
let hasErrors = false;
|
|
170
229
|
let data;
|
|
@@ -178,11 +237,9 @@ class ChoreographCreatePixel {
|
|
|
178
237
|
} catch (error) {
|
|
179
238
|
if (this.config.optional.includes(fieldName)) return undefined;
|
|
180
239
|
|
|
181
|
-
this.log(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
185
|
-
);
|
|
240
|
+
this.log("error", error.message, {
|
|
241
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
242
|
+
});
|
|
186
243
|
|
|
187
244
|
hasErrors = true;
|
|
188
245
|
}
|
|
@@ -195,11 +252,9 @@ class ChoreographCreatePixel {
|
|
|
195
252
|
(result == null || result === "") &&
|
|
196
253
|
!this.config.optional.includes(fieldName)
|
|
197
254
|
) {
|
|
198
|
-
this.log(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
202
|
-
);
|
|
255
|
+
this.log("error", "Value is empty", {
|
|
256
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
257
|
+
});
|
|
203
258
|
|
|
204
259
|
hasErrors = true;
|
|
205
260
|
}
|
|
@@ -207,17 +262,7 @@ class ChoreographCreatePixel {
|
|
|
207
262
|
return result;
|
|
208
263
|
};
|
|
209
264
|
|
|
210
|
-
if (this.config.type
|
|
211
|
-
data = {
|
|
212
|
-
conversion: handleField(this.config.scrape),
|
|
213
|
-
attribution: localStorage.getItem("create-attribution-id"),
|
|
214
|
-
};
|
|
215
|
-
|
|
216
|
-
if (typeof data.attribution !== "string") {
|
|
217
|
-
this.log("warn", "There's no attribution ID stored yet");
|
|
218
|
-
hasErrors = true;
|
|
219
|
-
}
|
|
220
|
-
} else if (this.config.type !== "scraper") {
|
|
265
|
+
if (this.config.type !== "scraper") {
|
|
221
266
|
data = handleField(this.config.scrape);
|
|
222
267
|
} else {
|
|
223
268
|
data = Object.keys(this.config.scrape).reduce(
|
|
@@ -234,19 +279,7 @@ class ChoreographCreatePixel {
|
|
|
234
279
|
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
235
280
|
try {
|
|
236
281
|
this.config.before(data, (newData) => {
|
|
237
|
-
if (
|
|
238
|
-
localStorage.setItem("create-attribution-id", data);
|
|
239
|
-
this.log("success", "Successful!", { attribution: data });
|
|
240
|
-
|
|
241
|
-
try {
|
|
242
|
-
this.config.after(data);
|
|
243
|
-
} catch (error) {
|
|
244
|
-
this.log("error", error.message, {
|
|
245
|
-
after: this.config.after,
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
} else if (Array.isArray(newData))
|
|
249
|
-
newData.forEach((id) => this.send(id));
|
|
282
|
+
if (Array.isArray(newData)) newData.forEach((id) => this.send(id));
|
|
250
283
|
else this.send(newData);
|
|
251
284
|
});
|
|
252
285
|
} catch (error) {
|
|
@@ -268,10 +301,20 @@ class ChoreographCreatePixel {
|
|
|
268
301
|
url = `https://d.lemonpi.io/scrapes${
|
|
269
302
|
this.config.debug ? "?validate=true" : ""
|
|
270
303
|
}`;
|
|
304
|
+
|
|
271
305
|
break;
|
|
272
306
|
|
|
273
307
|
case "conversion":
|
|
274
|
-
url =
|
|
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
|
+
|
|
275
318
|
break;
|
|
276
319
|
|
|
277
320
|
default:
|
|
@@ -283,6 +326,7 @@ class ChoreographCreatePixel {
|
|
|
283
326
|
sku: data,
|
|
284
327
|
})
|
|
285
328
|
)}`;
|
|
329
|
+
|
|
286
330
|
break;
|
|
287
331
|
}
|
|
288
332
|
|
package/dist/bundle.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
1
|
+
/*! choreograph-create-pixel v1.4.1 2023/02/01 */
|
|
2
2
|
class ChoreographCreatePixel {
|
|
3
3
|
constructor(userConfig) {
|
|
4
4
|
this.previouslyScrapedHash = null;
|
|
@@ -12,7 +12,7 @@ class ChoreographCreatePixel {
|
|
|
12
12
|
basketed: "π",
|
|
13
13
|
purchased: "π§Ύ",
|
|
14
14
|
attribution: "π",
|
|
15
|
-
conversion: "
|
|
15
|
+
conversion: "π",
|
|
16
16
|
},
|
|
17
17
|
debug: /(pixel|lemonpi)_debug/i.test(location.href),
|
|
18
18
|
before: (data, callback) => callback(data),
|
|
@@ -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.type} ${
|
|
87
|
-
this.config.
|
|
86
|
+
`%cβΈ¬ create%c ${this.config.icons[this.config.type]} ${this.config.type}${
|
|
87
|
+
this.config.label ? ` (${this.config.label})` : ""
|
|
88
88
|
} %c${message}`,
|
|
89
89
|
"background:black;color:white;border-radius:3px;padding:3px 6px",
|
|
90
90
|
"font-weight:bold",
|
|
@@ -102,16 +102,35 @@ class ChoreographCreatePixel {
|
|
|
102
102
|
advertiser: this.config.advertiser,
|
|
103
103
|
});
|
|
104
104
|
else if (
|
|
105
|
-
![
|
|
105
|
+
![
|
|
106
|
+
"scraper",
|
|
107
|
+
"viewed",
|
|
108
|
+
"basketed",
|
|
109
|
+
"purchased",
|
|
110
|
+
"attribution",
|
|
111
|
+
"conversion",
|
|
112
|
+
].includes(this.config.type)
|
|
106
113
|
)
|
|
107
|
-
this.log(
|
|
108
|
-
|
|
114
|
+
this.log(
|
|
115
|
+
"error",
|
|
116
|
+
"Please use scraper, viewed, basketed, purchased, attribution or conversion",
|
|
117
|
+
{ type: this.config.type }
|
|
118
|
+
);
|
|
119
|
+
else if (
|
|
120
|
+
["attribution", "conversion"].includes(this.config.type) &&
|
|
121
|
+
typeof this.config.label !== "string"
|
|
122
|
+
)
|
|
123
|
+
this.log("error", "Please use a string", {
|
|
124
|
+
label: this.config.label,
|
|
109
125
|
});
|
|
110
126
|
else if (!(this.config.url instanceof RegExp))
|
|
111
127
|
this.log("error", "Please use a regular expression", {
|
|
112
128
|
url: this.config.url,
|
|
113
129
|
});
|
|
114
|
-
else if (
|
|
130
|
+
else if (
|
|
131
|
+
!["attribution", "conversion"].includes(this.config.type) &&
|
|
132
|
+
!this.config.scrape
|
|
133
|
+
)
|
|
115
134
|
this.log("error", "Please provide something to scrape", {
|
|
116
135
|
scrape: this.config.scrape,
|
|
117
136
|
});
|
|
@@ -132,7 +151,8 @@ class ChoreographCreatePixel {
|
|
|
132
151
|
"This page has been translated by the browser, and won't be scraped"
|
|
133
152
|
);
|
|
134
153
|
else if (this.config.trigger) {
|
|
135
|
-
|
|
154
|
+
// TO-DO: replace this.config.type with a unique pixel instance ID
|
|
155
|
+
const elementAttribute = `create-${this.config.type}-${this.config.trigger.event}`;
|
|
136
156
|
|
|
137
157
|
try {
|
|
138
158
|
let elements =
|
|
@@ -143,12 +163,14 @@ class ChoreographCreatePixel {
|
|
|
143
163
|
if (!elements.forEach) elements = [elements];
|
|
144
164
|
|
|
145
165
|
elements.forEach((element) => {
|
|
146
|
-
if (!element.hasAttribute(
|
|
166
|
+
if (!element.hasAttribute(elementAttribute)) {
|
|
147
167
|
element.addEventListener(this.config.trigger.event, () =>
|
|
148
|
-
this.
|
|
168
|
+
this.config.type === "conversion"
|
|
169
|
+
? this.convert()
|
|
170
|
+
: this.scrape(element)
|
|
149
171
|
);
|
|
150
172
|
|
|
151
|
-
element.setAttribute(
|
|
173
|
+
element.setAttribute(elementAttribute, "");
|
|
152
174
|
}
|
|
153
175
|
});
|
|
154
176
|
} catch (error) {
|
|
@@ -157,12 +179,49 @@ class ChoreographCreatePixel {
|
|
|
157
179
|
});
|
|
158
180
|
}
|
|
159
181
|
} else {
|
|
160
|
-
this.
|
|
182
|
+
switch (this.config.type) {
|
|
183
|
+
case "attribution":
|
|
184
|
+
this.attribute();
|
|
185
|
+
break;
|
|
186
|
+
|
|
187
|
+
case "conversion":
|
|
188
|
+
this.convert();
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
default:
|
|
192
|
+
this.scrape();
|
|
193
|
+
break;
|
|
194
|
+
}
|
|
161
195
|
}
|
|
162
196
|
|
|
163
197
|
setTimeout(() => this.cycle(), 750);
|
|
164
198
|
}
|
|
165
199
|
|
|
200
|
+
attribute() {
|
|
201
|
+
const ccpid = this.constructor.getQueryParameter("ccpid");
|
|
202
|
+
if (!ccpid) return this.log("warn", "ccpid query parameter not present");
|
|
203
|
+
|
|
204
|
+
if (!/^[0-9a-f]{20}$/.test(ccpid))
|
|
205
|
+
return this.log(
|
|
206
|
+
"error",
|
|
207
|
+
"ccpid query parameter is not formatted correctly"
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
const storageItemLabel = `choreograph-${this.config.label}`;
|
|
211
|
+
const storedCcpid = localStorage.getItem(storageItemLabel);
|
|
212
|
+
if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
|
|
213
|
+
this.log("success", "Successful!", ccpid);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
convert() {
|
|
217
|
+
const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
|
|
218
|
+
|
|
219
|
+
if (!ccpid)
|
|
220
|
+
return this.log("warn", `"${this.config.label}" attribution not present`);
|
|
221
|
+
|
|
222
|
+
this.send(ccpid);
|
|
223
|
+
}
|
|
224
|
+
|
|
166
225
|
scrape(element) {
|
|
167
226
|
let hasErrors = false;
|
|
168
227
|
let data;
|
|
@@ -176,11 +235,9 @@ class ChoreographCreatePixel {
|
|
|
176
235
|
} catch (error) {
|
|
177
236
|
if (this.config.optional.includes(fieldName)) return undefined;
|
|
178
237
|
|
|
179
|
-
this.log(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
183
|
-
);
|
|
238
|
+
this.log("error", error.message, {
|
|
239
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
240
|
+
});
|
|
184
241
|
|
|
185
242
|
hasErrors = true;
|
|
186
243
|
}
|
|
@@ -193,11 +250,9 @@ class ChoreographCreatePixel {
|
|
|
193
250
|
(result == null || result === "") &&
|
|
194
251
|
!this.config.optional.includes(fieldName)
|
|
195
252
|
) {
|
|
196
|
-
this.log(
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
200
|
-
);
|
|
253
|
+
this.log("error", "Value is empty", {
|
|
254
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
255
|
+
});
|
|
201
256
|
|
|
202
257
|
hasErrors = true;
|
|
203
258
|
}
|
|
@@ -205,17 +260,7 @@ class ChoreographCreatePixel {
|
|
|
205
260
|
return result;
|
|
206
261
|
};
|
|
207
262
|
|
|
208
|
-
if (this.config.type
|
|
209
|
-
data = {
|
|
210
|
-
conversion: handleField(this.config.scrape),
|
|
211
|
-
attribution: localStorage.getItem("create-attribution-id"),
|
|
212
|
-
};
|
|
213
|
-
|
|
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") {
|
|
263
|
+
if (this.config.type !== "scraper") {
|
|
219
264
|
data = handleField(this.config.scrape);
|
|
220
265
|
} else {
|
|
221
266
|
data = Object.keys(this.config.scrape).reduce(
|
|
@@ -232,19 +277,7 @@ class ChoreographCreatePixel {
|
|
|
232
277
|
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
233
278
|
try {
|
|
234
279
|
this.config.before(data, (newData) => {
|
|
235
|
-
if (
|
|
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));
|
|
280
|
+
if (Array.isArray(newData)) newData.forEach((id) => this.send(id));
|
|
248
281
|
else this.send(newData);
|
|
249
282
|
});
|
|
250
283
|
} catch (error) {
|
|
@@ -266,10 +299,20 @@ class ChoreographCreatePixel {
|
|
|
266
299
|
url = `https://d.lemonpi.io/scrapes${
|
|
267
300
|
this.config.debug ? "?validate=true" : ""
|
|
268
301
|
}`;
|
|
302
|
+
|
|
269
303
|
break;
|
|
270
304
|
|
|
271
305
|
case "conversion":
|
|
272
|
-
url =
|
|
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
|
+
|
|
273
316
|
break;
|
|
274
317
|
|
|
275
318
|
default:
|
|
@@ -281,6 +324,7 @@ class ChoreographCreatePixel {
|
|
|
281
324
|
sku: data,
|
|
282
325
|
})
|
|
283
326
|
)}`;
|
|
327
|
+
|
|
284
328
|
break;
|
|
285
329
|
}
|
|
286
330
|
|
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 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:"
|
|
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}();
|
package/package.json
CHANGED
|
@@ -1,13 +1,15 @@
|
|
|
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
|
+
"version": "1.4.1",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"keywords": [
|
|
6
7
|
"wpp",
|
|
7
8
|
"groupm",
|
|
8
9
|
"lemonpi",
|
|
9
10
|
"odc",
|
|
10
|
-
"opendc"
|
|
11
|
+
"opendc",
|
|
12
|
+
"greenhouse"
|
|
11
13
|
],
|
|
12
14
|
"author": "Rick Stevens <rick.stevens@choreograph.com> (https://lemonpi.io)",
|
|
13
15
|
"repository": "gitlab:2sixty/choreograph-create/solutions/choreograph-create-pixel",
|
|
@@ -28,16 +30,18 @@
|
|
|
28
30
|
"prepublishOnly": "npm run build"
|
|
29
31
|
},
|
|
30
32
|
"devDependencies": {
|
|
31
|
-
"@babel/core": "^7.20.
|
|
33
|
+
"@babel/core": "^7.20.12",
|
|
34
|
+
"@babel/eslint-parser": "^7.19.1",
|
|
35
|
+
"@babel/plugin-syntax-import-assertions": "^7.20.0",
|
|
32
36
|
"@babel/preset-env": "^7.20.2",
|
|
33
37
|
"@rollup/plugin-babel": "^6.0.3",
|
|
34
|
-
"@rollup/plugin-eslint": "^9.0.
|
|
35
|
-
"@rollup/plugin-terser": "^0.
|
|
36
|
-
"eslint": "^8.
|
|
37
|
-
"eslint-config-prettier": "^8.
|
|
38
|
+
"@rollup/plugin-eslint": "^9.0.3",
|
|
39
|
+
"@rollup/plugin-terser": "^0.4.0",
|
|
40
|
+
"eslint": "^8.33.0",
|
|
41
|
+
"eslint-config-prettier": "^8.6.0",
|
|
38
42
|
"eslint-plugin-prettier": "^4.2.1",
|
|
39
43
|
"moment": "^2.29.4",
|
|
40
|
-
"prettier": "^2.8.
|
|
41
|
-
"rollup": "^
|
|
44
|
+
"prettier": "^2.8.3",
|
|
45
|
+
"rollup": "^3.12.0"
|
|
42
46
|
}
|
|
43
47
|
}
|