fck-honey 0.3.5 → 0.3.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,24 +1,28 @@
1
1
  # fck-honey
2
- Open source lib for Merchants to detect if a customer has Honey browser extension installed
2
+ Open source lib for Merchants to detect if a customer has Honey browser extension installed [ [Demo 🎥](https://youtu.be/Em9Fjil8Xds) ]
3
3
 
4
- <img width="968" height="532" alt="image" src="https://github.com/user-attachments/assets/b0bcccd6-d922-436d-8b0d-dcd4334a9219" />
4
+ <img width="1307" height="561" alt="image" src="https://github.com/user-attachments/assets/33b2554b-fc90-4b4b-b917-4ed088664200" />
5
5
 
6
- ## Inspiration
7
- [MegaLag exposed Honey as a scam](https://www.youtube.com/watch?v=wwB3FmbcC88)
6
+ ## Easy install (no-JS)
7
+ This will automatically listen for Honey and show a default warning to the user to disable the extension as shown above). Install this at the very top of the `<head>` in your webpage to ensure it runs prior to Honey.
8
+ ```html
9
+ <script src="https://cdn.jsdelivr.net/npm/fck-honey@latest/dist/auto.min.js"></script>
10
+ ```
8
11
 
9
- ## Usage (Browser Global)
12
+ ## Custom Usage (Browser Global)
10
13
 
11
14
  ```html
12
- <script src="https://cdn.jsdelivr.net/npm/fck-honey@0/dist/honey-detect.js"></script>
15
+ <script src="https://cdn.jsdelivr.net/npm/fck-honey@latest/dist/honey-detect.min.js"></script>
13
16
  <script>
14
- window.fckHoney.listen((warn) => {
17
+ window.fckHoney.listen((warn, el, vendor) => {
15
18
  // Decide how you want to handle this. Native warn function allows you to tell the user to disable Honey.
19
+ // vendor is "honey" or "Capital One Shopping"
16
20
  warn("You must disable the Honey extension to continue.");
17
21
  });
18
22
  </script>
19
23
  ```
20
24
 
21
- ## Usage (ESM)
25
+ ## Custom Usage (ESM)
22
26
 
23
27
  ```sh
24
28
  npm install fck-honey
@@ -27,8 +31,9 @@ npm install fck-honey
27
31
  ```js
28
32
  import { listen } from "fck-honey";
29
33
 
30
- listen((warn) => {
34
+ listen((warn, el, vendor) => {
31
35
  // Decide how you want to handle this. Native warn function allows you to tell the user to disable Honey.
36
+ // vendor is "honey" or "Capital One Shopping"
32
37
  warn("You must disable the Honey extension to continue.");
33
38
  });
34
39
  ```
@@ -36,9 +41,10 @@ listen((warn) => {
36
41
  ## Advanced Options
37
42
 
38
43
  ```js
39
- window.fckHoney.listen((warn, el) => {
44
+ window.fckHoney.listen((warn, el, vendor) => {
40
45
  // removeHoney defaults to true (element is auto-removed).
41
46
  // Set removeHoney to false if you want to keep the Honey element for some reason.
47
+ // vendor is "honey" or "Capital One Shopping"
42
48
  }, { removeHoney: false });
43
49
  ```
44
50
 
@@ -47,3 +53,6 @@ window.fckHoney.listen((warn) => {
47
53
  // Stop observing if nothing is detected within 10 seconds.
48
54
  }, { unbindAfterSeconds: 10 });
49
55
  ```
56
+
57
+ ## Inspiration
58
+ [MegaLag exposed Honey as a scam](https://www.youtube.com/watch?v=wwB3FmbcC88)
package/dist/auto.js ADDED
@@ -0,0 +1,285 @@
1
+ (function() {
2
+ // dist/bundle-tmp/version.js
3
+ var VERSION = "0.3.7";
4
+
5
+ // dist/bundle-tmp/core.js
6
+ var __assign = function() {
7
+ __assign = Object.assign || function(t) {
8
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
9
+ s = arguments[i];
10
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
11
+ t[p] = s[p];
12
+ }
13
+ return t;
14
+ };
15
+ return __assign.apply(this, arguments);
16
+ };
17
+ var version = VERSION;
18
+ var DEFAULT_Z_NEAR_MAX = 214748e4;
19
+ var UUIDISH_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
+ var TARGET_SELECTOR = "div";
21
+ var OVERLAY_STYLE_ID = "simple-overlay-styles";
22
+ function showOverlay(message) {
23
+ if (typeof document === "undefined")
24
+ return function() {
25
+ };
26
+ if (!document.getElementById(OVERLAY_STYLE_ID)) {
27
+ var style = document.createElement("style");
28
+ style.id = OVERLAY_STYLE_ID;
29
+ style.textContent = ".simple-overlay{position:fixed;inset:0;background:rgba(0,0,0,0.6);z-index:2147483647;display:flex;align-items:center;justify-content:center;pointer-events:all;}.simple-overlay-message{background:#ffffff;padding:16px 20px;border-radius:8px;font-size:14px;max-width:80%;text-align:center;box-shadow:0 10px 30px rgba(0,0,0,0.3);}";
30
+ document.head.appendChild(style);
31
+ }
32
+ var overlay = document.createElement("div");
33
+ overlay.className = "simple-overlay";
34
+ var messageEl = document.createElement("div");
35
+ messageEl.className = "simple-overlay-message";
36
+ messageEl.innerHTML = message;
37
+ overlay.appendChild(messageEl);
38
+ if (document.body) {
39
+ document.body.appendChild(overlay);
40
+ }
41
+ var prevOverflow = document.body ? document.body.style.overflow : "";
42
+ if (document.body)
43
+ document.body.style.overflow = "hidden";
44
+ return function hideOverlay() {
45
+ overlay.remove();
46
+ if (document.body)
47
+ document.body.style.overflow = prevOverflow;
48
+ };
49
+ }
50
+ function parseZIndex(cs, el) {
51
+ var computed = parseInt(cs.zIndex, 10);
52
+ if (isFinite(computed))
53
+ return computed;
54
+ var inline = parseInt(el.style.zIndex, 10);
55
+ return isFinite(inline) ? inline : null;
56
+ }
57
+ function getDataGuidAttribute(el) {
58
+ for (var i = 0; i < el.attributes.length; i += 1) {
59
+ var attr = el.attributes[i];
60
+ if (!attr)
61
+ continue;
62
+ if (attr.name.indexOf("data-") === 0) {
63
+ var suffix = attr.name.slice(5);
64
+ if (UUIDISH_RE.test(suffix) && attr.value === "true") {
65
+ return attr.name;
66
+ }
67
+ }
68
+ }
69
+ return null;
70
+ }
71
+ function hasNearMaxZIndex(el, zNearMax, debug) {
72
+ var inlineZ = parseInt(el.style.zIndex, 10);
73
+ if (!isFinite(inlineZ) || inlineZ < zNearMax) {
74
+ var cs_1 = getComputedStyle(el);
75
+ var z_1 = parseZIndex(cs_1, el);
76
+ if (z_1 === null || z_1 < zNearMax) {
77
+ if (debug)
78
+ console.log("+++ reject: z-index", z_1, cs_1.zIndex, el.style.zIndex, el);
79
+ return false;
80
+ }
81
+ if (cs_1.display === "none") {
82
+ if (debug)
83
+ console.log("+++ reject: display none", el);
84
+ return false;
85
+ }
86
+ if (el.shadowRoot) {
87
+ if (debug)
88
+ console.log("+++ reject: shadowRoot", el);
89
+ return false;
90
+ }
91
+ return true;
92
+ }
93
+ var cs = getComputedStyle(el);
94
+ var z = parseZIndex(cs, el);
95
+ if (z === null || z < zNearMax) {
96
+ if (debug)
97
+ console.log("+++ reject: z-index", z, cs.zIndex, el.style.zIndex, el);
98
+ return false;
99
+ }
100
+ if (cs.display === "none") {
101
+ if (debug)
102
+ console.log("+++ reject: display none", el);
103
+ return false;
104
+ }
105
+ if (el.shadowRoot) {
106
+ if (debug)
107
+ console.log("+++ reject: shadowRoot", el);
108
+ return false;
109
+ }
110
+ return true;
111
+ }
112
+ var VENDOR_MATCHERS = [
113
+ {
114
+ name: "honey",
115
+ matches: function(el, uuidGate, debug) {
116
+ if (!el.id) {
117
+ if (debug)
118
+ console.log("+++ reject: no id", el);
119
+ return false;
120
+ }
121
+ if (uuidGate && !UUIDISH_RE.test(el.id)) {
122
+ if (debug)
123
+ console.log("+++ reject: uuid", el.id);
124
+ return false;
125
+ }
126
+ return true;
127
+ }
128
+ },
129
+ {
130
+ name: "Capital One Shopping",
131
+ matches: function(el, _uuidGate, debug) {
132
+ var dataGuid = getDataGuidAttribute(el);
133
+ if (!dataGuid) {
134
+ if (debug)
135
+ console.log("+++ reject: no data guid", el);
136
+ return false;
137
+ }
138
+ if (debug)
139
+ console.log("+++ match capitalone", dataGuid, el);
140
+ return true;
141
+ }
142
+ }
143
+ ];
144
+ function getVendorForDiv(el, zNearMax, uuidGate, debug) {
145
+ if (!hasNearMaxZIndex(el, zNearMax, debug))
146
+ return null;
147
+ for (var i = 0; i < VENDOR_MATCHERS.length; i += 1) {
148
+ var matcher = VENDOR_MATCHERS[i];
149
+ if (matcher.matches(el, uuidGate, debug)) {
150
+ if (debug)
151
+ console.log("+++ match", matcher.name, el);
152
+ return matcher.name;
153
+ }
154
+ }
155
+ if (debug)
156
+ console.log("+++ reject: no vendor match", el);
157
+ return null;
158
+ }
159
+ function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
160
+ var _a;
161
+ if (el instanceof HTMLDivElement && el.matches(TARGET_SELECTOR)) {
162
+ var vendor = getVendorForDiv(el, zNearMax, uuidGate, debug);
163
+ if (seen.indexOf(el) === -1 && vendor) {
164
+ seen.push(el);
165
+ handleMatch(el, vendor);
166
+ }
167
+ }
168
+ var divs = (_a = el.querySelectorAll) === null || _a === void 0 ? void 0 : _a.call(el, TARGET_SELECTOR);
169
+ if (!divs)
170
+ return;
171
+ for (var i = 0; i < divs.length; i += 1) {
172
+ var d = divs[i];
173
+ var vendor = getVendorForDiv(d, zNearMax, uuidGate, debug);
174
+ if (seen.indexOf(d) === -1 && vendor) {
175
+ seen.push(d);
176
+ handleMatch(d, vendor);
177
+ }
178
+ }
179
+ }
180
+ function startHoneyOverlayObserver(options) {
181
+ var _a, _b, _c, _d, _e;
182
+ if (options === void 0) {
183
+ options = {};
184
+ }
185
+ var seen = [];
186
+ var zNearMax = (_a = options.zNearMax) !== null && _a !== void 0 ? _a : DEFAULT_Z_NEAR_MAX;
187
+ var uuidGate = (_b = options.uuidGate) !== null && _b !== void 0 ? _b : true;
188
+ var debug = (_c = options.debug) !== null && _c !== void 0 ? _c : false;
189
+ var removeHoney = (_d = options.removeHoney) !== null && _d !== void 0 ? _d : true;
190
+ var unbindAfterSeconds = options.unbindAfterSeconds;
191
+ var warn = function(message) {
192
+ return showOverlay(message);
193
+ };
194
+ var onMatch = (_e = options.onMatch) !== null && _e !== void 0 ? _e : (function() {
195
+ });
196
+ var matched = false;
197
+ var unbindTimer;
198
+ var handleMatch = function(el, vendor) {
199
+ matched = true;
200
+ if (typeof unbindTimer === "number") {
201
+ clearTimeout(unbindTimer);
202
+ unbindTimer = void 0;
203
+ }
204
+ if (removeHoney && el.parentNode)
205
+ el.parentNode.removeChild(el);
206
+ if (removeHoney) {
207
+ onMatch(warn, void 0, vendor);
208
+ } else {
209
+ onMatch(warn, el, vendor);
210
+ }
211
+ };
212
+ var mo = new MutationObserver(function(mutations) {
213
+ for (var _i = 0, mutations_1 = mutations; _i < mutations_1.length; _i++) {
214
+ var m = mutations_1[_i];
215
+ if (debug && document.body && (m.target === document.body || m.target instanceof Node && document.body.contains(m.target))) {
216
+ console.log("+++ body mutation", m.type, m.target);
217
+ }
218
+ }
219
+ for (var _a2 = 0, mutations_2 = mutations; _a2 < mutations_2.length; _a2++) {
220
+ var m = mutations_2[_a2];
221
+ if (m.type === "childList") {
222
+ if (m.addedNodes.length) {
223
+ for (var i = 0; i < m.addedNodes.length; i += 1) {
224
+ var node = m.addedNodes[i];
225
+ if (node.nodeType === Node.ELEMENT_NODE) {
226
+ scanElement(node, seen, zNearMax, uuidGate, debug, handleMatch);
227
+ }
228
+ }
229
+ }
230
+ if (m.target instanceof Element) {
231
+ scanElement(m.target, seen, zNearMax, uuidGate, debug, handleMatch);
232
+ }
233
+ } else if (m.type === "attributes") {
234
+ if (m.target instanceof Element) {
235
+ scanElement(m.target, seen, zNearMax, uuidGate, debug, handleMatch);
236
+ }
237
+ }
238
+ }
239
+ });
240
+ mo.observe(document.documentElement, {
241
+ subtree: true,
242
+ childList: true,
243
+ attributes: true,
244
+ attributeFilter: ["style", "class", "id"]
245
+ });
246
+ scanElement(document.documentElement, seen, zNearMax, uuidGate, debug, handleMatch);
247
+ if (typeof unbindAfterSeconds === "number" && unbindAfterSeconds > 0) {
248
+ unbindTimer = window.setTimeout(function() {
249
+ if (!matched) {
250
+ mo.disconnect();
251
+ }
252
+ }, unbindAfterSeconds * 1e3);
253
+ }
254
+ return {
255
+ stop: function() {
256
+ mo.disconnect();
257
+ if (typeof unbindTimer === "number") {
258
+ clearTimeout(unbindTimer);
259
+ }
260
+ }
261
+ };
262
+ }
263
+ function listen(onMatch, options) {
264
+ if (options === void 0) {
265
+ options = {};
266
+ }
267
+ return startHoneyOverlayObserver(__assign(__assign({}, options), { onMatch: onMatch }));
268
+ }
269
+
270
+ // dist/bundle-tmp/auto.js
271
+ function buildAutoModalHtml(vendor) {
272
+ var query = encodeURIComponent("Why is the " + vendor + " browser extension shady, and how can i uninstall it?");
273
+ var helpUrl = "http://chatgpt.com/?q=" + query;
274
+ return '<div class="modal" role="dialog" aria-modal="true" aria-labelledby="honey-modal-title"><h2 id="honey-modal-title">This site can\u2019t proceed with ' + vendor + ' enabled</h2><div style="display:flex;gap:8px;flex-wrap:wrap;margin:8px 0 12px 0;"><a href="' + helpUrl + '" target="_blank" rel="noopener noreferrer" style="display:block;padding:10px 12px;background:#e6eef7;color:#0b1b2b;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;border-radius:6px;flex:1 1 220px;">Please disable it to continue your checkout.</a><a href="https://www.youtube.com/watch?v=wwB3FmbcC88" target="_blank" rel="noopener noreferrer" style="display:block;padding:10px 12px;background:#e6eef7;color:#0b1b2b;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;border-radius:6px;flex:1 1 220px;">Here is why \uD83C\uDFA5</a></div></div>';
275
+ }
276
+ if (typeof window !== "undefined") {
277
+ window.fckHoney = window.fckHoney || {};
278
+ window.fckHoney.listen = listen;
279
+ window.fckHoney.version = version;
280
+ window.fckHoneyHandle = window.fckHoney.listen(function(warn, _el, vendor) {
281
+ var vendorLabel = vendor || "honey";
282
+ warn(buildAutoModalHtml(vendorLabel));
283
+ });
284
+ }
285
+ })();
@@ -0,0 +1,24 @@
1
+ import { listen, version } from "./core";
2
+ function buildAutoModalHtml(vendor) {
3
+ var query = encodeURIComponent("Why is the " + vendor + " browser extension shady, and how can i uninstall it?");
4
+ var helpUrl = "http://chatgpt.com/?q=" + query;
5
+ return ('<div class="modal" role="dialog" aria-modal="true" aria-labelledby="honey-modal-title">' +
6
+ '<h2 id="honey-modal-title">This site can’t proceed with ' +
7
+ vendor +
8
+ " enabled</h2>" +
9
+ '<div style="display:flex;gap:8px;flex-wrap:wrap;margin:8px 0 12px 0;">' +
10
+ '<a href="' +
11
+ helpUrl +
12
+ '" target="_blank" rel="noopener noreferrer" style="display:block;padding:10px 12px;background:#e6eef7;color:#0b1b2b;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;border-radius:6px;flex:1 1 220px;">Please disable it to continue your checkout.</a>' +
13
+ '<a href="https://www.youtube.com/watch?v=wwB3FmbcC88" target="_blank" rel="noopener noreferrer" style="display:block;padding:10px 12px;background:#e6eef7;color:#0b1b2b;text-decoration:underline;text-decoration-thickness:1px;text-underline-offset:3px;border-radius:6px;flex:1 1 220px;">Here is why 🎥</a>' +
14
+ "</div></div>");
15
+ }
16
+ if (typeof window !== "undefined") {
17
+ window.fckHoney = window.fckHoney || {};
18
+ window.fckHoney.listen = listen;
19
+ window.fckHoney.version = version;
20
+ window.fckHoneyHandle = window.fckHoney.listen(function (warn, _el, vendor) {
21
+ var vendorLabel = vendor || "honey";
22
+ warn(buildAutoModalHtml(vendorLabel));
23
+ });
24
+ }
@@ -13,7 +13,7 @@ import { VERSION } from "./version";
13
13
  export var version = VERSION;
14
14
  var DEFAULT_Z_NEAR_MAX = 2147480000;
15
15
  var UUIDISH_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16
- var TARGET_SELECTOR = "div[id]";
16
+ var TARGET_SELECTOR = "div";
17
17
  var OVERLAY_STYLE_ID = "simple-overlay-styles";
18
18
  function showOverlay(message) {
19
19
  if (typeof document === "undefined")
@@ -51,17 +51,21 @@ function parseZIndex(cs, el) {
51
51
  var inline = parseInt(el.style.zIndex, 10);
52
52
  return isFinite(inline) ? inline : null;
53
53
  }
54
- function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
55
- if (!el.id) {
56
- if (debug)
57
- console.log("+++ reject: no id", el);
58
- return false;
59
- }
60
- if (uuidGate && !UUIDISH_RE.test(el.id)) {
61
- if (debug)
62
- console.log("+++ reject: uuid", el.id);
63
- return false;
54
+ function getDataGuidAttribute(el) {
55
+ for (var i = 0; i < el.attributes.length; i += 1) {
56
+ var attr = el.attributes[i];
57
+ if (!attr)
58
+ continue;
59
+ if (attr.name.indexOf("data-") === 0) {
60
+ var suffix = attr.name.slice(5);
61
+ if (UUIDISH_RE.test(suffix) && attr.value === "true") {
62
+ return attr.name;
63
+ }
64
+ }
64
65
  }
66
+ return null;
67
+ }
68
+ function hasNearMaxZIndex(el, zNearMax, debug) {
65
69
  // Fast path: if inline z-index is missing or below threshold, skip expensive getComputedStyle
66
70
  var inlineZ = parseInt(el.style.zIndex, 10);
67
71
  if (!isFinite(inlineZ) || inlineZ < zNearMax) {
@@ -82,8 +86,6 @@ function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
82
86
  console.log("+++ reject: shadowRoot", el);
83
87
  return false;
84
88
  }
85
- if (debug)
86
- console.log("+++ match", el);
87
89
  return true;
88
90
  }
89
91
  var cs = getComputedStyle(el);
@@ -103,16 +105,63 @@ function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
103
105
  console.log("+++ reject: shadowRoot", el);
104
106
  return false;
105
107
  }
106
- if (debug)
107
- console.log("+++ match", el);
108
108
  return true;
109
109
  }
110
+ // Each matcher should only check vendor-specific flags; shared z-index gating happens earlier.
111
+ var VENDOR_MATCHERS = [
112
+ {
113
+ name: "honey",
114
+ matches: function (el, uuidGate, debug) {
115
+ if (!el.id) {
116
+ if (debug)
117
+ console.log("+++ reject: no id", el);
118
+ return false;
119
+ }
120
+ if (uuidGate && !UUIDISH_RE.test(el.id)) {
121
+ if (debug)
122
+ console.log("+++ reject: uuid", el.id);
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ },
128
+ {
129
+ name: "Capital One Shopping",
130
+ matches: function (el, _uuidGate, debug) {
131
+ var dataGuid = getDataGuidAttribute(el);
132
+ if (!dataGuid) {
133
+ if (debug)
134
+ console.log("+++ reject: no data guid", el);
135
+ return false;
136
+ }
137
+ if (debug)
138
+ console.log("+++ match capitalone", dataGuid, el);
139
+ return true;
140
+ }
141
+ }
142
+ ];
143
+ function getVendorForDiv(el, zNearMax, uuidGate, debug) {
144
+ if (!hasNearMaxZIndex(el, zNearMax, debug))
145
+ return null;
146
+ for (var i = 0; i < VENDOR_MATCHERS.length; i += 1) {
147
+ var matcher = VENDOR_MATCHERS[i];
148
+ if (matcher.matches(el, uuidGate, debug)) {
149
+ if (debug)
150
+ console.log("+++ match", matcher.name, el);
151
+ return matcher.name;
152
+ }
153
+ }
154
+ if (debug)
155
+ console.log("+++ reject: no vendor match", el);
156
+ return null;
157
+ }
110
158
  function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
111
159
  var _a;
112
160
  if (el instanceof HTMLDivElement && el.matches(TARGET_SELECTOR)) {
113
- if (seen.indexOf(el) === -1 && looksLikeTargetDiv(el, zNearMax, uuidGate, debug)) {
161
+ var vendor = getVendorForDiv(el, zNearMax, uuidGate, debug);
162
+ if (seen.indexOf(el) === -1 && vendor) {
114
163
  seen.push(el);
115
- handleMatch(el);
164
+ handleMatch(el, vendor);
116
165
  }
117
166
  }
118
167
  var divs = (_a = el.querySelectorAll) === null || _a === void 0 ? void 0 : _a.call(el, TARGET_SELECTOR);
@@ -120,9 +169,10 @@ function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
120
169
  return;
121
170
  for (var i = 0; i < divs.length; i += 1) {
122
171
  var d = divs[i];
123
- if (seen.indexOf(d) === -1 && looksLikeTargetDiv(d, zNearMax, uuidGate, debug)) {
172
+ var vendor = getVendorForDiv(d, zNearMax, uuidGate, debug);
173
+ if (seen.indexOf(d) === -1 && vendor) {
124
174
  seen.push(d);
125
- handleMatch(d);
175
+ handleMatch(d, vendor);
126
176
  }
127
177
  }
128
178
  }
@@ -139,7 +189,7 @@ export function startHoneyOverlayObserver(options) {
139
189
  var onMatch = (_e = options.onMatch) !== null && _e !== void 0 ? _e : (function () { });
140
190
  var matched = false;
141
191
  var unbindTimer;
142
- var handleMatch = function (el) {
192
+ var handleMatch = function (el, vendor) {
143
193
  matched = true;
144
194
  if (typeof unbindTimer === "number") {
145
195
  clearTimeout(unbindTimer);
@@ -148,10 +198,10 @@ export function startHoneyOverlayObserver(options) {
148
198
  if (removeHoney && el.parentNode)
149
199
  el.parentNode.removeChild(el);
150
200
  if (removeHoney) {
151
- onMatch(warn);
201
+ onMatch(warn, undefined, vendor);
152
202
  }
153
203
  else {
154
- onMatch(warn, el);
204
+ onMatch(warn, el, vendor);
155
205
  }
156
206
  };
157
207
  // Greedy, page-wide observer: Honey overlays can be inserted late, moved,
@@ -1,2 +1,2 @@
1
1
  // Auto-generated from package.json. Do not edit by hand.
2
- export var VERSION = "0.3.5";
2
+ export var VERSION = "0.3.7";
package/dist/esm/core.js CHANGED
@@ -13,7 +13,7 @@ import { VERSION } from "./version";
13
13
  export var version = VERSION;
14
14
  var DEFAULT_Z_NEAR_MAX = 2147480000;
15
15
  var UUIDISH_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
16
- var TARGET_SELECTOR = "div[id]";
16
+ var TARGET_SELECTOR = "div";
17
17
  var OVERLAY_STYLE_ID = "simple-overlay-styles";
18
18
  function showOverlay(message) {
19
19
  if (typeof document === "undefined")
@@ -51,17 +51,21 @@ function parseZIndex(cs, el) {
51
51
  var inline = parseInt(el.style.zIndex, 10);
52
52
  return isFinite(inline) ? inline : null;
53
53
  }
54
- function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
55
- if (!el.id) {
56
- if (debug)
57
- console.log("+++ reject: no id", el);
58
- return false;
59
- }
60
- if (uuidGate && !UUIDISH_RE.test(el.id)) {
61
- if (debug)
62
- console.log("+++ reject: uuid", el.id);
63
- return false;
54
+ function getDataGuidAttribute(el) {
55
+ for (var i = 0; i < el.attributes.length; i += 1) {
56
+ var attr = el.attributes[i];
57
+ if (!attr)
58
+ continue;
59
+ if (attr.name.indexOf("data-") === 0) {
60
+ var suffix = attr.name.slice(5);
61
+ if (UUIDISH_RE.test(suffix) && attr.value === "true") {
62
+ return attr.name;
63
+ }
64
+ }
64
65
  }
66
+ return null;
67
+ }
68
+ function hasNearMaxZIndex(el, zNearMax, debug) {
65
69
  // Fast path: if inline z-index is missing or below threshold, skip expensive getComputedStyle
66
70
  var inlineZ = parseInt(el.style.zIndex, 10);
67
71
  if (!isFinite(inlineZ) || inlineZ < zNearMax) {
@@ -82,8 +86,6 @@ function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
82
86
  console.log("+++ reject: shadowRoot", el);
83
87
  return false;
84
88
  }
85
- if (debug)
86
- console.log("+++ match", el);
87
89
  return true;
88
90
  }
89
91
  var cs = getComputedStyle(el);
@@ -103,16 +105,63 @@ function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
103
105
  console.log("+++ reject: shadowRoot", el);
104
106
  return false;
105
107
  }
106
- if (debug)
107
- console.log("+++ match", el);
108
108
  return true;
109
109
  }
110
+ // Each matcher should only check vendor-specific flags; shared z-index gating happens earlier.
111
+ var VENDOR_MATCHERS = [
112
+ {
113
+ name: "honey",
114
+ matches: function (el, uuidGate, debug) {
115
+ if (!el.id) {
116
+ if (debug)
117
+ console.log("+++ reject: no id", el);
118
+ return false;
119
+ }
120
+ if (uuidGate && !UUIDISH_RE.test(el.id)) {
121
+ if (debug)
122
+ console.log("+++ reject: uuid", el.id);
123
+ return false;
124
+ }
125
+ return true;
126
+ }
127
+ },
128
+ {
129
+ name: "Capital One Shopping",
130
+ matches: function (el, _uuidGate, debug) {
131
+ var dataGuid = getDataGuidAttribute(el);
132
+ if (!dataGuid) {
133
+ if (debug)
134
+ console.log("+++ reject: no data guid", el);
135
+ return false;
136
+ }
137
+ if (debug)
138
+ console.log("+++ match capitalone", dataGuid, el);
139
+ return true;
140
+ }
141
+ }
142
+ ];
143
+ function getVendorForDiv(el, zNearMax, uuidGate, debug) {
144
+ if (!hasNearMaxZIndex(el, zNearMax, debug))
145
+ return null;
146
+ for (var i = 0; i < VENDOR_MATCHERS.length; i += 1) {
147
+ var matcher = VENDOR_MATCHERS[i];
148
+ if (matcher.matches(el, uuidGate, debug)) {
149
+ if (debug)
150
+ console.log("+++ match", matcher.name, el);
151
+ return matcher.name;
152
+ }
153
+ }
154
+ if (debug)
155
+ console.log("+++ reject: no vendor match", el);
156
+ return null;
157
+ }
110
158
  function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
111
159
  var _a;
112
160
  if (el instanceof HTMLDivElement && el.matches(TARGET_SELECTOR)) {
113
- if (seen.indexOf(el) === -1 && looksLikeTargetDiv(el, zNearMax, uuidGate, debug)) {
161
+ var vendor = getVendorForDiv(el, zNearMax, uuidGate, debug);
162
+ if (seen.indexOf(el) === -1 && vendor) {
114
163
  seen.push(el);
115
- handleMatch(el);
164
+ handleMatch(el, vendor);
116
165
  }
117
166
  }
118
167
  var divs = (_a = el.querySelectorAll) === null || _a === void 0 ? void 0 : _a.call(el, TARGET_SELECTOR);
@@ -120,9 +169,10 @@ function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
120
169
  return;
121
170
  for (var i = 0; i < divs.length; i += 1) {
122
171
  var d = divs[i];
123
- if (seen.indexOf(d) === -1 && looksLikeTargetDiv(d, zNearMax, uuidGate, debug)) {
172
+ var vendor = getVendorForDiv(d, zNearMax, uuidGate, debug);
173
+ if (seen.indexOf(d) === -1 && vendor) {
124
174
  seen.push(d);
125
- handleMatch(d);
175
+ handleMatch(d, vendor);
126
176
  }
127
177
  }
128
178
  }
@@ -139,7 +189,7 @@ export function startHoneyOverlayObserver(options) {
139
189
  var onMatch = (_e = options.onMatch) !== null && _e !== void 0 ? _e : (function () { });
140
190
  var matched = false;
141
191
  var unbindTimer;
142
- var handleMatch = function (el) {
192
+ var handleMatch = function (el, vendor) {
143
193
  matched = true;
144
194
  if (typeof unbindTimer === "number") {
145
195
  clearTimeout(unbindTimer);
@@ -148,10 +198,10 @@ export function startHoneyOverlayObserver(options) {
148
198
  if (removeHoney && el.parentNode)
149
199
  el.parentNode.removeChild(el);
150
200
  if (removeHoney) {
151
- onMatch(warn);
201
+ onMatch(warn, undefined, vendor);
152
202
  }
153
203
  else {
154
- onMatch(warn, el);
204
+ onMatch(warn, el, vendor);
155
205
  }
156
206
  };
157
207
  // Greedy, page-wide observer: Honey overlays can be inserted late, moved,
@@ -1,2 +1,2 @@
1
1
  // Auto-generated from package.json. Do not edit by hand.
2
- export var VERSION = "0.3.5";
2
+ export var VERSION = "0.3.7";
@@ -1,6 +1,6 @@
1
1
  (function() {
2
2
  // dist/bundle-tmp/version.js
3
- var VERSION = "0.3.5";
3
+ var VERSION = "0.3.7";
4
4
 
5
5
  // dist/bundle-tmp/core.js
6
6
  var __assign = function() {
@@ -17,7 +17,7 @@
17
17
  var version = VERSION;
18
18
  var DEFAULT_Z_NEAR_MAX = 214748e4;
19
19
  var UUIDISH_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
20
- var TARGET_SELECTOR = "div[id]";
20
+ var TARGET_SELECTOR = "div";
21
21
  var OVERLAY_STYLE_ID = "simple-overlay-styles";
22
22
  function showOverlay(message) {
23
23
  if (typeof document === "undefined")
@@ -54,17 +54,21 @@
54
54
  var inline = parseInt(el.style.zIndex, 10);
55
55
  return isFinite(inline) ? inline : null;
56
56
  }
57
- function looksLikeTargetDiv(el, zNearMax, uuidGate, debug) {
58
- if (!el.id) {
59
- if (debug)
60
- console.log("+++ reject: no id", el);
61
- return false;
62
- }
63
- if (uuidGate && !UUIDISH_RE.test(el.id)) {
64
- if (debug)
65
- console.log("+++ reject: uuid", el.id);
66
- return false;
57
+ function getDataGuidAttribute(el) {
58
+ for (var i = 0; i < el.attributes.length; i += 1) {
59
+ var attr = el.attributes[i];
60
+ if (!attr)
61
+ continue;
62
+ if (attr.name.indexOf("data-") === 0) {
63
+ var suffix = attr.name.slice(5);
64
+ if (UUIDISH_RE.test(suffix) && attr.value === "true") {
65
+ return attr.name;
66
+ }
67
+ }
67
68
  }
69
+ return null;
70
+ }
71
+ function hasNearMaxZIndex(el, zNearMax, debug) {
68
72
  var inlineZ = parseInt(el.style.zIndex, 10);
69
73
  if (!isFinite(inlineZ) || inlineZ < zNearMax) {
70
74
  var cs_1 = getComputedStyle(el);
@@ -84,8 +88,6 @@
84
88
  console.log("+++ reject: shadowRoot", el);
85
89
  return false;
86
90
  }
87
- if (debug)
88
- console.log("+++ match", el);
89
91
  return true;
90
92
  }
91
93
  var cs = getComputedStyle(el);
@@ -105,16 +107,62 @@
105
107
  console.log("+++ reject: shadowRoot", el);
106
108
  return false;
107
109
  }
108
- if (debug)
109
- console.log("+++ match", el);
110
110
  return true;
111
111
  }
112
+ var VENDOR_MATCHERS = [
113
+ {
114
+ name: "honey",
115
+ matches: function(el, uuidGate, debug) {
116
+ if (!el.id) {
117
+ if (debug)
118
+ console.log("+++ reject: no id", el);
119
+ return false;
120
+ }
121
+ if (uuidGate && !UUIDISH_RE.test(el.id)) {
122
+ if (debug)
123
+ console.log("+++ reject: uuid", el.id);
124
+ return false;
125
+ }
126
+ return true;
127
+ }
128
+ },
129
+ {
130
+ name: "Capital One Shopping",
131
+ matches: function(el, _uuidGate, debug) {
132
+ var dataGuid = getDataGuidAttribute(el);
133
+ if (!dataGuid) {
134
+ if (debug)
135
+ console.log("+++ reject: no data guid", el);
136
+ return false;
137
+ }
138
+ if (debug)
139
+ console.log("+++ match capitalone", dataGuid, el);
140
+ return true;
141
+ }
142
+ }
143
+ ];
144
+ function getVendorForDiv(el, zNearMax, uuidGate, debug) {
145
+ if (!hasNearMaxZIndex(el, zNearMax, debug))
146
+ return null;
147
+ for (var i = 0; i < VENDOR_MATCHERS.length; i += 1) {
148
+ var matcher = VENDOR_MATCHERS[i];
149
+ if (matcher.matches(el, uuidGate, debug)) {
150
+ if (debug)
151
+ console.log("+++ match", matcher.name, el);
152
+ return matcher.name;
153
+ }
154
+ }
155
+ if (debug)
156
+ console.log("+++ reject: no vendor match", el);
157
+ return null;
158
+ }
112
159
  function scanElement(el, seen, zNearMax, uuidGate, debug, handleMatch) {
113
160
  var _a;
114
161
  if (el instanceof HTMLDivElement && el.matches(TARGET_SELECTOR)) {
115
- if (seen.indexOf(el) === -1 && looksLikeTargetDiv(el, zNearMax, uuidGate, debug)) {
162
+ var vendor = getVendorForDiv(el, zNearMax, uuidGate, debug);
163
+ if (seen.indexOf(el) === -1 && vendor) {
116
164
  seen.push(el);
117
- handleMatch(el);
165
+ handleMatch(el, vendor);
118
166
  }
119
167
  }
120
168
  var divs = (_a = el.querySelectorAll) === null || _a === void 0 ? void 0 : _a.call(el, TARGET_SELECTOR);
@@ -122,9 +170,10 @@
122
170
  return;
123
171
  for (var i = 0; i < divs.length; i += 1) {
124
172
  var d = divs[i];
125
- if (seen.indexOf(d) === -1 && looksLikeTargetDiv(d, zNearMax, uuidGate, debug)) {
173
+ var vendor = getVendorForDiv(d, zNearMax, uuidGate, debug);
174
+ if (seen.indexOf(d) === -1 && vendor) {
126
175
  seen.push(d);
127
- handleMatch(d);
176
+ handleMatch(d, vendor);
128
177
  }
129
178
  }
130
179
  }
@@ -146,7 +195,7 @@
146
195
  });
147
196
  var matched = false;
148
197
  var unbindTimer;
149
- var handleMatch = function(el) {
198
+ var handleMatch = function(el, vendor) {
150
199
  matched = true;
151
200
  if (typeof unbindTimer === "number") {
152
201
  clearTimeout(unbindTimer);
@@ -155,9 +204,9 @@
155
204
  if (removeHoney && el.parentNode)
156
205
  el.parentNode.removeChild(el);
157
206
  if (removeHoney) {
158
- onMatch(warn);
207
+ onMatch(warn, void 0, vendor);
159
208
  } else {
160
- onMatch(warn, el);
209
+ onMatch(warn, el, vendor);
161
210
  }
162
211
  };
163
212
  var mo = new MutationObserver(function(mutations) {
@@ -1,6 +1,7 @@
1
- export declare const version = "0.3.5";
1
+ export declare const version = "0.3.7";
2
2
  export type WarnCallback = (message: string) => () => void;
3
- export type MatchCallback = (warn: WarnCallback, el?: HTMLDivElement) => void;
3
+ export type DetectedVendor = "honey" | "Capital One Shopping";
4
+ export type MatchCallback = (warn: WarnCallback, el?: HTMLDivElement, vendor?: DetectedVendor) => void;
4
5
  export interface ObserverOptions {
5
6
  onMatch?: MatchCallback;
6
7
  uuidGate?: boolean;
@@ -1 +1 @@
1
- export declare const VERSION = "0.3.5";
1
+ export declare const VERSION = "0.3.7";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fck-honey",
3
- "version": "0.3.5",
3
+ "version": "0.3.7",
4
4
  "description": "Detects Honey browser extension overlays for merchants.",
5
5
  "license": "MIT",
6
6
  "main": "dist/honey-detect.js",
@@ -18,7 +18,7 @@
18
18
  ],
19
19
  "scripts": {
20
20
  "prebuild": "node scripts/write-version.cjs",
21
- "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.bundle.json && esbuild dist/bundle-tmp/global.js --bundle --format=iife --platform=browser --target=es5 --outfile=dist/honey-detect.js",
21
+ "build": "tsc -p tsconfig.esm.json && tsc -p tsconfig.bundle.json && esbuild dist/bundle-tmp/global.js --bundle --format=iife --platform=browser --target=es5 --outfile=dist/honey-detect.js && esbuild dist/bundle-tmp/auto.js --bundle --format=iife --platform=browser --target=es5 --outfile=dist/auto.js",
22
22
  "prepublishOnly": "npm run build && node scripts/check-version.cjs",
23
23
  "clean": "rm -rf dist"
24
24
  },