choreograph-create-pixel 1.3.2 β 1.4.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 +35 -5
- package/dist/bundle.cjs.js +100 -51
- package/dist/bundle.esm.js +100 -51
- 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.0 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,53 @@ 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 (
|
|
207
|
+
!/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(
|
|
208
|
+
ccpid
|
|
209
|
+
)
|
|
210
|
+
)
|
|
211
|
+
return this.log(
|
|
212
|
+
"error",
|
|
213
|
+
"ccpid query parameter is not formatted correctly"
|
|
214
|
+
);
|
|
215
|
+
|
|
216
|
+
const storageItemLabel = `choreograph-${this.config.label}`;
|
|
217
|
+
const storedCcpid = localStorage.getItem(storageItemLabel);
|
|
218
|
+
if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
|
|
219
|
+
this.log("success", "Successful!", ccpid);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
convert() {
|
|
223
|
+
const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
|
|
224
|
+
|
|
225
|
+
if (!ccpid)
|
|
226
|
+
return this.log("warn", `"${this.config.label}" attribution not present`);
|
|
227
|
+
|
|
228
|
+
this.send(ccpid);
|
|
229
|
+
}
|
|
230
|
+
|
|
168
231
|
scrape(element) {
|
|
169
232
|
let hasErrors = false;
|
|
170
233
|
let data;
|
|
@@ -178,11 +241,9 @@ class ChoreographCreatePixel {
|
|
|
178
241
|
} catch (error) {
|
|
179
242
|
if (this.config.optional.includes(fieldName)) return undefined;
|
|
180
243
|
|
|
181
|
-
this.log(
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
185
|
-
);
|
|
244
|
+
this.log("error", error.message, {
|
|
245
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
246
|
+
});
|
|
186
247
|
|
|
187
248
|
hasErrors = true;
|
|
188
249
|
}
|
|
@@ -191,14 +252,13 @@ class ChoreographCreatePixel {
|
|
|
191
252
|
if (typeof result === "string")
|
|
192
253
|
result = result.replace(/\s+/g, " ").trim();
|
|
193
254
|
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
);
|
|
255
|
+
if (
|
|
256
|
+
(result == null || result === "") &&
|
|
257
|
+
!this.config.optional.includes(fieldName)
|
|
258
|
+
) {
|
|
259
|
+
this.log("error", "Value is empty", {
|
|
260
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
261
|
+
});
|
|
202
262
|
|
|
203
263
|
hasErrors = true;
|
|
204
264
|
}
|
|
@@ -206,17 +266,7 @@ class ChoreographCreatePixel {
|
|
|
206
266
|
return result;
|
|
207
267
|
};
|
|
208
268
|
|
|
209
|
-
if (this.config.type
|
|
210
|
-
data = {
|
|
211
|
-
conversion: handleField(this.config.scrape),
|
|
212
|
-
attribution: localStorage.getItem("create-attribution-id"),
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
if (typeof data.attribution !== "string") {
|
|
216
|
-
this.log("warn", "There's no attribution ID stored yet");
|
|
217
|
-
hasErrors = true;
|
|
218
|
-
}
|
|
219
|
-
} else if (this.config.type !== "scraper") {
|
|
269
|
+
if (this.config.type !== "scraper") {
|
|
220
270
|
data = handleField(this.config.scrape);
|
|
221
271
|
} else {
|
|
222
272
|
data = Object.keys(this.config.scrape).reduce(
|
|
@@ -233,19 +283,7 @@ class ChoreographCreatePixel {
|
|
|
233
283
|
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
234
284
|
try {
|
|
235
285
|
this.config.before(data, (newData) => {
|
|
236
|
-
if (
|
|
237
|
-
localStorage.setItem("create-attribution-id", data);
|
|
238
|
-
this.log("success", "Successful!", { attribution: data });
|
|
239
|
-
|
|
240
|
-
try {
|
|
241
|
-
this.config.after(data);
|
|
242
|
-
} catch (error) {
|
|
243
|
-
this.log("error", error.message, {
|
|
244
|
-
after: this.config.after,
|
|
245
|
-
});
|
|
246
|
-
}
|
|
247
|
-
} else if (Array.isArray(newData))
|
|
248
|
-
newData.forEach((id) => this.send(id));
|
|
286
|
+
if (Array.isArray(newData)) newData.forEach((id) => this.send(id));
|
|
249
287
|
else this.send(newData);
|
|
250
288
|
});
|
|
251
289
|
} catch (error) {
|
|
@@ -267,10 +305,20 @@ class ChoreographCreatePixel {
|
|
|
267
305
|
url = `https://d.lemonpi.io/scrapes${
|
|
268
306
|
this.config.debug ? "?validate=true" : ""
|
|
269
307
|
}`;
|
|
308
|
+
|
|
270
309
|
break;
|
|
271
310
|
|
|
272
311
|
case "conversion":
|
|
273
|
-
url =
|
|
312
|
+
url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
|
|
313
|
+
JSON.stringify({
|
|
314
|
+
version: 1,
|
|
315
|
+
type: "conversion",
|
|
316
|
+
name: this.config.label,
|
|
317
|
+
"conversion-attribution-id": data,
|
|
318
|
+
"advertiser-id": this.config.advertiser,
|
|
319
|
+
})
|
|
320
|
+
)}`;
|
|
321
|
+
|
|
274
322
|
break;
|
|
275
323
|
|
|
276
324
|
default:
|
|
@@ -282,6 +330,7 @@ class ChoreographCreatePixel {
|
|
|
282
330
|
sku: data,
|
|
283
331
|
})
|
|
284
332
|
)}`;
|
|
333
|
+
|
|
285
334
|
break;
|
|
286
335
|
}
|
|
287
336
|
|
package/dist/bundle.esm.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! choreograph-create-pixel v1.
|
|
1
|
+
/*! choreograph-create-pixel v1.4.0 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,53 @@ 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 (
|
|
205
|
+
!/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(
|
|
206
|
+
ccpid
|
|
207
|
+
)
|
|
208
|
+
)
|
|
209
|
+
return this.log(
|
|
210
|
+
"error",
|
|
211
|
+
"ccpid query parameter is not formatted correctly"
|
|
212
|
+
);
|
|
213
|
+
|
|
214
|
+
const storageItemLabel = `choreograph-${this.config.label}`;
|
|
215
|
+
const storedCcpid = localStorage.getItem(storageItemLabel);
|
|
216
|
+
if (storedCcpid !== ccpid) localStorage.setItem(storageItemLabel, ccpid);
|
|
217
|
+
this.log("success", "Successful!", ccpid);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
convert() {
|
|
221
|
+
const ccpid = localStorage.getItem(`choreograph-${this.config.label}`);
|
|
222
|
+
|
|
223
|
+
if (!ccpid)
|
|
224
|
+
return this.log("warn", `"${this.config.label}" attribution not present`);
|
|
225
|
+
|
|
226
|
+
this.send(ccpid);
|
|
227
|
+
}
|
|
228
|
+
|
|
166
229
|
scrape(element) {
|
|
167
230
|
let hasErrors = false;
|
|
168
231
|
let data;
|
|
@@ -176,11 +239,9 @@ class ChoreographCreatePixel {
|
|
|
176
239
|
} catch (error) {
|
|
177
240
|
if (this.config.optional.includes(fieldName)) return undefined;
|
|
178
241
|
|
|
179
|
-
this.log(
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
{ [fieldName ? `scrape.${fieldName}` : "scrape"]: result }
|
|
183
|
-
);
|
|
242
|
+
this.log("error", error.message, {
|
|
243
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
244
|
+
});
|
|
184
245
|
|
|
185
246
|
hasErrors = true;
|
|
186
247
|
}
|
|
@@ -189,14 +250,13 @@ class ChoreographCreatePixel {
|
|
|
189
250
|
if (typeof result === "string")
|
|
190
251
|
result = result.replace(/\s+/g, " ").trim();
|
|
191
252
|
|
|
192
|
-
if (
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
);
|
|
253
|
+
if (
|
|
254
|
+
(result == null || result === "") &&
|
|
255
|
+
!this.config.optional.includes(fieldName)
|
|
256
|
+
) {
|
|
257
|
+
this.log("error", "Value is empty", {
|
|
258
|
+
[fieldName ? `scrape.${fieldName}` : "scrape"]: result,
|
|
259
|
+
});
|
|
200
260
|
|
|
201
261
|
hasErrors = true;
|
|
202
262
|
}
|
|
@@ -204,17 +264,7 @@ class ChoreographCreatePixel {
|
|
|
204
264
|
return result;
|
|
205
265
|
};
|
|
206
266
|
|
|
207
|
-
if (this.config.type
|
|
208
|
-
data = {
|
|
209
|
-
conversion: handleField(this.config.scrape),
|
|
210
|
-
attribution: localStorage.getItem("create-attribution-id"),
|
|
211
|
-
};
|
|
212
|
-
|
|
213
|
-
if (typeof data.attribution !== "string") {
|
|
214
|
-
this.log("warn", "There's no attribution ID stored yet");
|
|
215
|
-
hasErrors = true;
|
|
216
|
-
}
|
|
217
|
-
} else if (this.config.type !== "scraper") {
|
|
267
|
+
if (this.config.type !== "scraper") {
|
|
218
268
|
data = handleField(this.config.scrape);
|
|
219
269
|
} else {
|
|
220
270
|
data = Object.keys(this.config.scrape).reduce(
|
|
@@ -231,19 +281,7 @@ class ChoreographCreatePixel {
|
|
|
231
281
|
if (!hasErrors && this.previouslyScrapedHash !== dataHash) {
|
|
232
282
|
try {
|
|
233
283
|
this.config.before(data, (newData) => {
|
|
234
|
-
if (
|
|
235
|
-
localStorage.setItem("create-attribution-id", data);
|
|
236
|
-
this.log("success", "Successful!", { attribution: data });
|
|
237
|
-
|
|
238
|
-
try {
|
|
239
|
-
this.config.after(data);
|
|
240
|
-
} catch (error) {
|
|
241
|
-
this.log("error", error.message, {
|
|
242
|
-
after: this.config.after,
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
} else if (Array.isArray(newData))
|
|
246
|
-
newData.forEach((id) => this.send(id));
|
|
284
|
+
if (Array.isArray(newData)) newData.forEach((id) => this.send(id));
|
|
247
285
|
else this.send(newData);
|
|
248
286
|
});
|
|
249
287
|
} catch (error) {
|
|
@@ -265,10 +303,20 @@ class ChoreographCreatePixel {
|
|
|
265
303
|
url = `https://d.lemonpi.io/scrapes${
|
|
266
304
|
this.config.debug ? "?validate=true" : ""
|
|
267
305
|
}`;
|
|
306
|
+
|
|
268
307
|
break;
|
|
269
308
|
|
|
270
309
|
case "conversion":
|
|
271
|
-
url =
|
|
310
|
+
url = `https://content.lemonpi.io/track/event?e=${encodeURIComponent(
|
|
311
|
+
JSON.stringify({
|
|
312
|
+
version: 1,
|
|
313
|
+
type: "conversion",
|
|
314
|
+
name: this.config.label,
|
|
315
|
+
"conversion-attribution-id": data,
|
|
316
|
+
"advertiser-id": this.config.advertiser,
|
|
317
|
+
})
|
|
318
|
+
)}`;
|
|
319
|
+
|
|
272
320
|
break;
|
|
273
321
|
|
|
274
322
|
default:
|
|
@@ -280,6 +328,7 @@ class ChoreographCreatePixel {
|
|
|
280
328
|
sku: data,
|
|
281
329
|
})
|
|
282
330
|
)}`;
|
|
331
|
+
|
|
283
332
|
break;
|
|
284
333
|
}
|
|
285
334
|
|
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.0 2023/02/01 */
|
|
2
|
+
var ChoreographCreatePixel=function(){"use strict";function e(e,t){var r=Object.keys(e);if(Object.getOwnPropertySymbols){var n=Object.getOwnPropertySymbols(e);t&&(n=n.filter((function(t){return Object.getOwnPropertyDescriptor(e,t).enumerable}))),r.push.apply(r,n)}return r}function t(t){for(var r=1;r<arguments.length;r++){var i=null!=arguments[r]?arguments[r]:{};r%2?e(Object(i),!0).forEach((function(e){n(t,e,i[e])})):Object.getOwnPropertyDescriptors?Object.defineProperties(t,Object.getOwnPropertyDescriptors(i)):e(Object(i)).forEach((function(e){Object.defineProperty(t,e,Object.getOwnPropertyDescriptor(i,e))}))}return t}function r(e,t){for(var r=0;r<t.length;r++){var n=t[r];n.enumerable=n.enumerable||!1,n.configurable=!0,"value"in n&&(n.writable=!0),Object.defineProperty(e,i(n.key),n)}}function n(e,t,r){return(t=i(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}function i(e){var t=function(e,t){if("object"!=typeof e||null===e)return e;var r=e[Symbol.toPrimitive];if(void 0!==r){var n=r.call(e,t||"default");if("object"!=typeof n)return n;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===t?String:Number)(e)}(e,"string");return"symbol"==typeof t?t:String(t)}var o=function(){function e(r){!function(e,t){if(!(e instanceof t))throw new TypeError("Cannot call a class as a function")}(this,e),this.previouslyScrapedHash=null,this.logs=[],this.config=t({colors:{error:"red",warn:"orange",success:"green"},icons:{scraper:"π",viewed:"π",basketed:"π",purchased:"π§Ύ",attribution:"π",conversion:"π"},debug:/(pixel|lemonpi)_debug/i.test(location.href),before:function(e,t){return t(e)},after:function(){},optional:[]},r),this.validateConfig()&&this.cycle()}var i,o,c;return i=e,o=[{key:"log",value:function(e,t){var r,n=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;if(this.config.debug&&!this.logs.includes(t)){var i=["%cβΈ¬ create%c ".concat(this.config.icons[this.config.type]," ").concat(this.config.type).concat(this.config.label?" (".concat(this.config.label,")"):""," %c").concat(t),"background:black;color:white;border-radius:3px;padding:3px 6px","font-weight:bold","color:".concat(this.config.colors[e])];n&&i.push(n),(r=console).info.apply(r,i),this.logs.push(t)}}},{key:"validateConfig",value:function(){if("number"!=typeof this.config.advertiser)this.log("error","Please use a number",{advertiser:this.config.advertiser});else if(["scraper","viewed","basketed","purchased","attribution","conversion"].includes(this.config.type))if(["attribution","conversion"].includes(this.config.type)&&"string"!=typeof this.config.label)this.log("error","Please use a string",{label:this.config.label});else if(this.config.url instanceof RegExp){if(["attribution","conversion"].includes(this.config.type)||this.config.scrape)return!0;this.log("error","Please provide something to scrape",{scrape:this.config.scrape})}else this.log("error","Please use a regular expression",{url:this.config.url});else this.log("error","Please use scraper, viewed, basketed, purchased, attribution or conversion",{type:this.config.type})}},{key:"cycle",value:function(){var e=this;if(this.config.url.test(location.href))if("scraper"===this.config.type&&document.querySelector('html[class*="translated-"]'))this.log("warn","This page has been translated by the browser, and won't be scraped");else if(this.config.trigger){var t="create-".concat(this.config.type,"-").concat(this.config.trigger.event);try{var r="string"==typeof this.config.trigger.elements?document.querySelectorAll(this.config.trigger.elements):this.config.trigger.elements();r.forEach||(r=[r]),r.forEach((function(r){r.hasAttribute(t)||(r.addEventListener(e.config.trigger.event,(function(){return"conversion"===e.config.type?e.convert():e.scrape(r)})),r.setAttribute(t,""))}))}catch(e){this.log("error",e.message,{"trigger.elements":this.config.trigger.elements})}}else switch(this.config.type){case"attribution":this.attribute();break;case"conversion":this.convert();break;default:this.scrape()}else this.log("warn",'URL pattern does not match "'.concat(location.href,'"'),{url:this.config.url});setTimeout((function(){return e.cycle()}),750)}},{key:"attribute",value:function(){var e=this.constructor.getQueryParameter("ccpid");if(!e)return this.log("warn","ccpid query parameter not present");if(!/[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}/.test(e))return this.log("error","ccpid query parameter is not formatted correctly");var t="choreograph-".concat(this.config.label);localStorage.getItem(t)!==e&&localStorage.setItem(t,e),this.log("success","Successful!",e)}},{key:"convert",value:function(){var e=localStorage.getItem("choreograph-".concat(this.config.label));if(!e)return this.log("warn",'"'.concat(this.config.label,'" attribution not present'));this.send(e)}},{key:"scrape",value:function(e){var r,i=this,o=!1,c=function(t,r){var c=t;if("function"==typeof c)try{c=c(e)}catch(e){if(i.config.optional.includes(r))return;i.log("error",e.message,n({},r?"scrape.".concat(r):"scrape",c)),o=!0}return"string"==typeof c&&(c=c.replace(/\s+/g," ").trim()),null!=c&&""!==c||i.config.optional.includes(r)||(i.log("error","Value is empty",n({},r?"scrape.".concat(r):"scrape",c)),o=!0),c};r="scraper"!==this.config.type?c(this.config.scrape):Object.keys(this.config.scrape).reduce((function(e,r){return t(t({},e),{},n({},r,c(i.config.scrape[r],r)))}),{});var s=JSON.stringify(r);if(!o&&this.previouslyScrapedHash!==s){try{this.config.before(r,(function(e){Array.isArray(e)?e.forEach((function(e){return i.send(e)})):i.send(e)}))}catch(e){this.log("error",e.message,{before:this.config.before})}this.previouslyScrapedHash=s,this.logs.length=0}}},{key:"send",value:function(e){var r,n=this;switch(this.config.type){case"scraper":r="https://d.lemonpi.io/scrapes".concat(this.config.debug?"?validate=true":"");break;case"conversion":r="https://content.lemonpi.io/track/event?e=".concat(encodeURIComponent(JSON.stringify({version:1,type:"conversion",name:this.config.label,"conversion-attribution-id":e,"advertiser-id":this.config.advertiser})));break;default:r="https://d.lemonpi.io/a/".concat(this.config.advertiser,"/product/event?e=").concat(encodeURIComponent(JSON.stringify({"event-type":"product-".concat(this.config.type),sku:e})))}"scraper"===this.config.type||this.config.debug?fetch(r,"scraper"===this.config.type?{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({"advertiser-id":this.config.advertiser,sku:e.sku,fields:t(t({},e),{},{sku:void 0})})}:null).then((function(t){return t.json().then((function(r){if(t.ok){n.log("warn","Successful, with warnings. Details:",r);try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(t.status," (").concat(t.statusText,"). Details:"),r);n.logs.length=0})).catch((function(){if(t.ok){n.log("success","Successful!",["viewed","basketed","purchased"].includes(n.config.type)?{sku:e}:e);try{n.config.after(e)}catch(e){n.log("error",e.message,{after:n.config.after})}}else n.log("error","Failed: ".concat(t.status," (").concat(t.statusText,"). Details:"),t);n.logs.length=0}))})).catch((function(e){n.log("error","Failed: ".concat(e.message)),n.logs.length=0})):(new Image).src=r}}],c=[{key:"getUrl",value:function(){var e=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{},t=e.allowedQueryParameters,r=void 0===t?[]:t,n=e.allowHash,i=void 0!==n&&n,o="".concat(location.protocol,"//").concat(location.host).concat(location.pathname),c=new URLSearchParams(location.search);return r.reduce((function(e,t){var r=e?"&":"?",n=encodeURI(t),i=c.get(t);return null!=i?(o+="".concat(r).concat(n).concat(""===i?"":"=".concat(encodeURI(i))),!0):e}),!1),i&&(o+=location.hash),o}},{key:"getAllPathSegments",value:function(){return location.pathname.split("/").filter((function(e){return e})).map((function(e){return decodeURI(e)}))}},{key:"getPathSegment",value:function(e){return this.getAllPathSegments()[e]}},{key:"getAllQueryParameters",value:function(){return location.search.replace(/^\?/,"").split("&").filter((function(e){return e})).reduce((function(e,r){return t(t({},e),{},n({},decodeURI(r.split("=")[0]),decodeURI(r.split("=")[1]||"")))}),{})}},{key:"getQueryParameter",value:function(e){return this.getAllQueryParameters()[e]}}],o&&r(i.prototype,o),c&&r(i,c),Object.defineProperty(i,"prototype",{writable:!1}),e}();return o}();
|
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.0",
|
|
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
|
}
|