@gemx-dev/clarity-visualize 0.8.62 → 0.8.64
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/build/clarity.visualize.js +228 -115
- package/build/clarity.visualize.min.js +1 -1
- package/build/clarity.visualize.module.js +228 -115
- package/package.json +2 -2
- package/src/heatmap.ts +65 -6
- package/src/visualizer.ts +14 -2
- package/types/visualize.d.ts +2 -0
|
@@ -67,105 +67,6 @@ function __generator(thisArg, body) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
var _a$1;
|
|
71
|
-
var DataHelper = /** @class */ (function () {
|
|
72
|
-
function DataHelper(state) {
|
|
73
|
-
var _this = this;
|
|
74
|
-
this.regionMap = {};
|
|
75
|
-
this.regions = {};
|
|
76
|
-
this.metrics = {};
|
|
77
|
-
this.lean = false;
|
|
78
|
-
this.reset = function () {
|
|
79
|
-
_this.metrics = {};
|
|
80
|
-
_this.lean = false;
|
|
81
|
-
_this.regions = {};
|
|
82
|
-
_this.regionMap = {};
|
|
83
|
-
};
|
|
84
|
-
this.metric = function (event) {
|
|
85
|
-
var _a;
|
|
86
|
-
if (_this.state.options.metadata) {
|
|
87
|
-
var metricMarkup = [];
|
|
88
|
-
var regionMarkup = [];
|
|
89
|
-
// Copy over metrics for future reference
|
|
90
|
-
for (var m in event.data) {
|
|
91
|
-
var eventType = typeof event.data[m];
|
|
92
|
-
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
93
|
-
if (!(m in _this.metrics)) {
|
|
94
|
-
_this.metrics[m] = 0;
|
|
95
|
-
}
|
|
96
|
-
var key = parseInt(m, 10);
|
|
97
|
-
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
98
|
-
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
99
|
-
_this.metrics[m] = value;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
_this.metrics[m] += value;
|
|
103
|
-
}
|
|
104
|
-
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
for (var entry in _this.metrics) {
|
|
108
|
-
if (entry in DataHelper.METRIC_MAP) {
|
|
109
|
-
var m = _this.metrics[entry];
|
|
110
|
-
var map = DataHelper.METRIC_MAP[entry];
|
|
111
|
-
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
112
|
-
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
// Append region information to metadata
|
|
116
|
-
for (var name_1 in _this.regions) {
|
|
117
|
-
var r = _this.regions[name_1];
|
|
118
|
-
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
119
|
-
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
120
|
-
}
|
|
121
|
-
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
122
|
-
}
|
|
123
|
-
};
|
|
124
|
-
this.key = function (unit) {
|
|
125
|
-
switch (unit) {
|
|
126
|
-
case "html-price":
|
|
127
|
-
case "ld-price":
|
|
128
|
-
case "cls":
|
|
129
|
-
return "" /* Data.Constant.Empty */;
|
|
130
|
-
default: return unit;
|
|
131
|
-
}
|
|
132
|
-
};
|
|
133
|
-
this.value = function (num, unit) {
|
|
134
|
-
switch (unit) {
|
|
135
|
-
case "KB": return Math.round(num / 1024);
|
|
136
|
-
case "s": return Math.round(num / 10) / 100;
|
|
137
|
-
case "cls": return num / 1000;
|
|
138
|
-
case "html-price": return num / 100;
|
|
139
|
-
default: return num;
|
|
140
|
-
}
|
|
141
|
-
};
|
|
142
|
-
this.state = state;
|
|
143
|
-
}
|
|
144
|
-
DataHelper.prototype.region = function (event) {
|
|
145
|
-
var data = event.data;
|
|
146
|
-
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
147
|
-
var r = data_1[_i];
|
|
148
|
-
if (!(r.name in this.regions)) {
|
|
149
|
-
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
150
|
-
}
|
|
151
|
-
this.regionMap[r.id] = r.name;
|
|
152
|
-
}
|
|
153
|
-
};
|
|
154
|
-
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
155
|
-
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
156
|
-
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
157
|
-
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
158
|
-
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
159
|
-
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
160
|
-
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
161
|
-
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
162
|
-
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
163
|
-
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
164
|
-
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
165
|
-
_a$1);
|
|
166
|
-
return DataHelper;
|
|
167
|
-
}());
|
|
168
|
-
|
|
169
70
|
// tslint:disable: no-bitwise
|
|
170
71
|
function hash (input, precision) {
|
|
171
72
|
if (precision === void 0) { precision = null; }
|
|
@@ -186,12 +87,27 @@ function hash (input, precision) {
|
|
|
186
87
|
hash = Math.abs(hashOne + (hashTwo * 11579));
|
|
187
88
|
return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
|
|
188
89
|
}
|
|
90
|
+
|
|
91
|
+
function __spreadArray(to, from, pack) {
|
|
92
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
93
|
+
if (ar || !(i in from)) {
|
|
94
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
95
|
+
ar[i] = from[i];
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
99
|
+
}
|
|
189
100
|
|
|
190
101
|
var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
|
|
102
|
+
var extraExcludeClassNames = [];
|
|
191
103
|
var selectorMap = {};
|
|
192
104
|
function reset$8() {
|
|
193
105
|
selectorMap = {};
|
|
194
106
|
}
|
|
107
|
+
function config$2(classNames) {
|
|
108
|
+
extraExcludeClassNames = classNames || [];
|
|
109
|
+
console.log("[Selector] config called \u2014 extraExcludeClassNames set to: [".concat(extraExcludeClassNames.join(', '), "]"));
|
|
110
|
+
}
|
|
195
111
|
function get$1(input, type) {
|
|
196
112
|
var a = input.attributes;
|
|
197
113
|
var prefix = input.prefix ? input.prefix[type] : null;
|
|
@@ -214,7 +130,13 @@ function get$1(input, type) {
|
|
|
214
130
|
input.tag = input.tag.indexOf("svg:" /* Constant.SvgPrefix */) === 0 ? input.tag.substr("svg:" /* Constant.SvgPrefix */.length) : input.tag;
|
|
215
131
|
var selector = "".concat(prefix).concat(input.tag).concat(suffix);
|
|
216
132
|
var id = "id" /* Constant.Id */ in a && a["id" /* Constant.Id */].length > 0 ? a["id" /* Constant.Id */] : null;
|
|
217
|
-
var
|
|
133
|
+
var rawClasses = "class" /* Constant.Class */ in a ? a["class" /* Constant.Class */].trim().split(/\s+/) : [];
|
|
134
|
+
var filteredClasses = rawClasses.filter(function (c) { return filter(c); });
|
|
135
|
+
var removedClasses = rawClasses.filter(function (c) { return !filter(c); });
|
|
136
|
+
if (removedClasses.length > 0) {
|
|
137
|
+
console.log("[Selector] tag=\"".concat(input.tag, "\" id=\"").concat(input.id, "\" \u2014 removed classes: [").concat(removedClasses.join(', '), "] | kept: [").concat(filteredClasses.join(', '), "] | extraExclude: [").concat(extraExcludeClassNames.join(', '), "]"));
|
|
138
|
+
}
|
|
139
|
+
var classes = input.tag !== "BODY" /* Constant.BodyTag */ && filteredClasses.length > 0 ? filteredClasses.join("." /* Constant.Period */) : null;
|
|
218
140
|
if (classes && classes.length > 0) {
|
|
219
141
|
if (type === 0 /* Selector.Alpha */) {
|
|
220
142
|
// In Alpha mode, update selector to use class names, with relative positioning within the parent id container.
|
|
@@ -227,16 +149,19 @@ function get$1(input, type) {
|
|
|
227
149
|
selectorMap[key].push(input.id);
|
|
228
150
|
}
|
|
229
151
|
selector = "".concat(key).concat("~" /* Constant.Tilde */).concat(selectorMap[key].indexOf(input.id));
|
|
152
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
|
|
230
153
|
}
|
|
231
154
|
else {
|
|
232
155
|
// In Beta mode, we continue to look at query selectors in context of the full page
|
|
233
156
|
selector = "".concat(prefix).concat(input.tag, ".").concat(classes).concat(suffix);
|
|
157
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
|
|
234
158
|
}
|
|
235
159
|
}
|
|
236
160
|
// Update selector to use "id" field when available. There are two exceptions:
|
|
237
161
|
// (1) if "id" appears to be an auto generated string token, e.g. guid or a random id containing digits
|
|
238
162
|
// (2) if "id" appears inside a shadow DOM, in which case we continue to prefix up to shadow DOM to prevent conflicts
|
|
239
163
|
selector = id && filter(id) ? "".concat(getDomPrefix(prefix)).concat("#" /* Constant.Hash */).concat(id) : selector;
|
|
164
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ get ~ selector:", selector);
|
|
240
165
|
return selector;
|
|
241
166
|
}
|
|
242
167
|
}
|
|
@@ -263,6 +188,8 @@ function filter(value) {
|
|
|
263
188
|
if (!value) {
|
|
264
189
|
return false;
|
|
265
190
|
} // Do not process empty strings
|
|
191
|
+
var excludeClassNames = getExcludeClassNames();
|
|
192
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ filter ~ excludeClassNames:", excludeClassNames);
|
|
266
193
|
if (excludeClassNames.some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
|
|
267
194
|
return false;
|
|
268
195
|
}
|
|
@@ -274,9 +201,15 @@ function filter(value) {
|
|
|
274
201
|
}
|
|
275
202
|
return true;
|
|
276
203
|
}
|
|
204
|
+
function getExcludeClassNames() {
|
|
205
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ getExcludeClassNames ~ extraExcludeClassNames:", extraExcludeClassNames);
|
|
206
|
+
console.log("\uD83D\uDE80 \uD83D\uDC25 ~ getExcludeClassNames ~ excludeClassNames:", excludeClassNames);
|
|
207
|
+
return __spreadArray(__spreadArray([], excludeClassNames, true), extraExcludeClassNames, true);
|
|
208
|
+
}
|
|
277
209
|
|
|
278
210
|
var selector = /*#__PURE__*/Object.freeze({
|
|
279
211
|
__proto__: null,
|
|
212
|
+
config: config$2,
|
|
280
213
|
get: get$1,
|
|
281
214
|
reset: reset$8
|
|
282
215
|
});
|
|
@@ -312,6 +245,105 @@ function lookup(hash) {
|
|
|
312
245
|
|
|
313
246
|
var helper = { hash: hash, selector: selector, get: get$2, getNode: getNode, lookup: lookup };
|
|
314
247
|
|
|
248
|
+
var _a$1;
|
|
249
|
+
var DataHelper = /** @class */ (function () {
|
|
250
|
+
function DataHelper(state) {
|
|
251
|
+
var _this = this;
|
|
252
|
+
this.regionMap = {};
|
|
253
|
+
this.regions = {};
|
|
254
|
+
this.metrics = {};
|
|
255
|
+
this.lean = false;
|
|
256
|
+
this.reset = function () {
|
|
257
|
+
_this.metrics = {};
|
|
258
|
+
_this.lean = false;
|
|
259
|
+
_this.regions = {};
|
|
260
|
+
_this.regionMap = {};
|
|
261
|
+
};
|
|
262
|
+
this.metric = function (event) {
|
|
263
|
+
var _a;
|
|
264
|
+
if (_this.state.options.metadata) {
|
|
265
|
+
var metricMarkup = [];
|
|
266
|
+
var regionMarkup = [];
|
|
267
|
+
// Copy over metrics for future reference
|
|
268
|
+
for (var m in event.data) {
|
|
269
|
+
var eventType = typeof event.data[m];
|
|
270
|
+
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
271
|
+
if (!(m in _this.metrics)) {
|
|
272
|
+
_this.metrics[m] = 0;
|
|
273
|
+
}
|
|
274
|
+
var key = parseInt(m, 10);
|
|
275
|
+
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
276
|
+
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
277
|
+
_this.metrics[m] = value;
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
_this.metrics[m] += value;
|
|
281
|
+
}
|
|
282
|
+
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
for (var entry in _this.metrics) {
|
|
286
|
+
if (entry in DataHelper.METRIC_MAP) {
|
|
287
|
+
var m = _this.metrics[entry];
|
|
288
|
+
var map = DataHelper.METRIC_MAP[entry];
|
|
289
|
+
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
290
|
+
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
// Append region information to metadata
|
|
294
|
+
for (var name_1 in _this.regions) {
|
|
295
|
+
var r = _this.regions[name_1];
|
|
296
|
+
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
297
|
+
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
298
|
+
}
|
|
299
|
+
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
300
|
+
}
|
|
301
|
+
};
|
|
302
|
+
this.key = function (unit) {
|
|
303
|
+
switch (unit) {
|
|
304
|
+
case "html-price":
|
|
305
|
+
case "ld-price":
|
|
306
|
+
case "cls":
|
|
307
|
+
return "" /* Data.Constant.Empty */;
|
|
308
|
+
default: return unit;
|
|
309
|
+
}
|
|
310
|
+
};
|
|
311
|
+
this.value = function (num, unit) {
|
|
312
|
+
switch (unit) {
|
|
313
|
+
case "KB": return Math.round(num / 1024);
|
|
314
|
+
case "s": return Math.round(num / 10) / 100;
|
|
315
|
+
case "cls": return num / 1000;
|
|
316
|
+
case "html-price": return num / 100;
|
|
317
|
+
default: return num;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
this.state = state;
|
|
321
|
+
}
|
|
322
|
+
DataHelper.prototype.region = function (event) {
|
|
323
|
+
var data = event.data;
|
|
324
|
+
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
325
|
+
var r = data_1[_i];
|
|
326
|
+
if (!(r.name in this.regions)) {
|
|
327
|
+
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
328
|
+
}
|
|
329
|
+
this.regionMap[r.id] = r.name;
|
|
330
|
+
}
|
|
331
|
+
};
|
|
332
|
+
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
333
|
+
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
334
|
+
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
335
|
+
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
336
|
+
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
337
|
+
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
338
|
+
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
339
|
+
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
340
|
+
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
341
|
+
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
342
|
+
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
343
|
+
_a$1);
|
|
344
|
+
return DataHelper;
|
|
345
|
+
}());
|
|
346
|
+
|
|
315
347
|
var EnrichHelper = /** @class */ (function () {
|
|
316
348
|
function EnrichHelper() {
|
|
317
349
|
var _this = this;
|
|
@@ -356,6 +388,10 @@ var EnrichHelper = /** @class */ (function () {
|
|
|
356
388
|
var selectorBeta = helper.selector.get(input, 1 /* Layout.Selector.Beta */);
|
|
357
389
|
d.selectorBeta = selectorBeta.length > 0 ? selectorBeta : null;
|
|
358
390
|
d.hashBeta = selectorBeta.length > 0 ? helper.hash(d.selectorBeta) : null;
|
|
391
|
+
var rawClass = (d.attributes || {})['class'] || '';
|
|
392
|
+
if (rawClass.includes('gp_ls-is-cached')) {
|
|
393
|
+
console.log("[Enrich] id=".concat(d.id, " tag=").concat(d.tag, " | class=\"").concat(rawClass, "\" | selectorBeta=\"").concat(selectorBeta, "\" | hashBeta=").concat(d.hashBeta));
|
|
394
|
+
}
|
|
359
395
|
/* Track state for future reference */
|
|
360
396
|
node.alpha = selectorAlpha;
|
|
361
397
|
node.beta = selectorBeta;
|
|
@@ -882,12 +918,28 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
882
918
|
var points = {};
|
|
883
919
|
var localMax = 0;
|
|
884
920
|
var height = _this.state.window && _this.state.window.document ? _this.state.window.document.documentElement.clientHeight : 0;
|
|
921
|
+
// DEBUG LOG
|
|
922
|
+
console.group("[Heatmap] transform \u2014 total elements: ".concat(_this.data.length, ", height: ").concat(height, ", max: ").concat(_this.max));
|
|
885
923
|
for (var _i = 0, _a = _this.data; _i < _a.length; _i++) {
|
|
886
924
|
var element = _a[_i];
|
|
887
925
|
var el = _this.layout.get(element.hash);
|
|
926
|
+
if (!el) {
|
|
927
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 element not found in DOM"));
|
|
928
|
+
continue;
|
|
929
|
+
}
|
|
930
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
931
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 getBoundingClientRect not available"), el);
|
|
932
|
+
continue;
|
|
933
|
+
}
|
|
934
|
+
var r = el.getBoundingClientRect();
|
|
935
|
+
var isDebugHash = element.hash === "270qs0pw7";
|
|
936
|
+
var v = _this.visible(el, r, height, isDebugHash ? element.hash : undefined);
|
|
937
|
+
if (!v && _this.max !== null) {
|
|
938
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 not visible on re-render"), { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: _this.max !== null });
|
|
939
|
+
continue;
|
|
940
|
+
}
|
|
888
941
|
if (el && typeof el.getBoundingClientRect === "function") {
|
|
889
|
-
|
|
890
|
-
var v = _this.visible(el, r, height);
|
|
942
|
+
console.log("[Heatmap] PROCESS hash=\"".concat(element.hash, "\" visible=").concat(v, " points=").concat(element.points), { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
|
|
891
943
|
// Process clicks for only visible elements
|
|
892
944
|
if (_this.max === null || v) {
|
|
893
945
|
for (var i = 0; i < element.points; i++) {
|
|
@@ -896,6 +948,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
896
948
|
var k = "".concat(x).concat("X" /* Constant.Separator */).concat(y).concat("X" /* Constant.Separator */).concat(v ? 1 : 0);
|
|
897
949
|
points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
|
|
898
950
|
localMax = Math.max(points[k], localMax);
|
|
951
|
+
console.log(" point[".concat(i, "] x=").concat(x, " y=").concat(y, " visible=").concat(v, " clicks=").concat(element.clicks[i]));
|
|
899
952
|
}
|
|
900
953
|
}
|
|
901
954
|
}
|
|
@@ -903,6 +956,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
903
956
|
// Set the max value from the firs t
|
|
904
957
|
_this.max = _this.max ? _this.max : localMax;
|
|
905
958
|
// Once all points are accounted for, convert everything into absolute (x, y)
|
|
959
|
+
var skippedInvisible = 0;
|
|
906
960
|
for (var _b = 0, _c = Object.keys(points); _b < _c.length; _b++) {
|
|
907
961
|
var coordinates = _c[_b];
|
|
908
962
|
var parts = coordinates.split("X" /* Constant.Separator */);
|
|
@@ -910,16 +964,36 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
910
964
|
if (parts[2] === "1") {
|
|
911
965
|
output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
|
|
912
966
|
}
|
|
967
|
+
else {
|
|
968
|
+
skippedInvisible++;
|
|
969
|
+
}
|
|
913
970
|
}
|
|
971
|
+
console.log("[Heatmap] Result \u2014 drawn: ".concat(output.length, ", skipped invisible: ").concat(skippedInvisible, ", max: ").concat(_this.max));
|
|
972
|
+
console.groupEnd();
|
|
914
973
|
return output;
|
|
915
974
|
};
|
|
916
|
-
this.visible = function (el, r, height) {
|
|
975
|
+
this.visible = function (el, r, height, debugHash) {
|
|
917
976
|
var doc = _this.state.window.document;
|
|
918
977
|
var visibility = r.height > height ? true : false;
|
|
978
|
+
if (debugHash) {
|
|
979
|
+
console.group("[Heatmap visible] hash=".concat(debugHash, " tag=").concat(el === null || el === void 0 ? void 0 : el.tagName));
|
|
980
|
+
console.log(" rect: top=".concat(r.top.toFixed(1), " bottom=").concat(r.bottom.toFixed(1), " left=").concat(r.left.toFixed(1), " width=").concat(r.width.toFixed(1), " height=").concat(r.height.toFixed(1)));
|
|
981
|
+
console.log(" viewport height=".concat(height, " | r.height > height? ").concat(r.height > height));
|
|
982
|
+
}
|
|
919
983
|
if (visibility === false && r.width > 0 && r.height > 0) {
|
|
984
|
+
var checkX = r.left + (r.width / 2);
|
|
985
|
+
var checkY = r.top + (r.height / 2);
|
|
986
|
+
if (debugHash) {
|
|
987
|
+
console.log(" elementsFromPoint(".concat(checkX.toFixed(1), ", ").concat(checkY.toFixed(1), ")"));
|
|
988
|
+
}
|
|
920
989
|
while (!visibility && doc) {
|
|
921
990
|
var shadowElement = null;
|
|
922
|
-
var elements = doc.elementsFromPoint(
|
|
991
|
+
var elements = doc.elementsFromPoint(checkX, checkY);
|
|
992
|
+
if (debugHash) {
|
|
993
|
+
var topElements = elements.slice(0, 5).map(function (e) { return "".concat(e.tagName).concat(e.id ? '#' + e.id : '').concat(e.className ? '.' + String(e.className).trim().split(/\s+/).slice(0, 2).join('.') : ''); });
|
|
994
|
+
console.log(" top elements at center: [".concat(topElements.join(', '), "]"));
|
|
995
|
+
console.log(" is el in top elements? ".concat(elements.includes(el), " | el matches first non-canvas? ").concat(elements.find(function (e) { return !(e.tagName === "CANVAS" /* Constant.Canvas */ || (e.id && e.id.indexOf("clarity-" /* Constant.ClarityPrefix */) === 0)); }) === el));
|
|
996
|
+
}
|
|
923
997
|
for (var _i = 0, elements_1 = elements; _i < elements_1.length; _i++) {
|
|
924
998
|
var e = elements_1[_i];
|
|
925
999
|
// Ignore if top element ends up being the canvas element we added for heatmap visualization
|
|
@@ -933,7 +1007,18 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
933
1007
|
doc = shadowElement;
|
|
934
1008
|
}
|
|
935
1009
|
}
|
|
936
|
-
|
|
1010
|
+
else if (debugHash) {
|
|
1011
|
+
if (r.width === 0 || r.height === 0) {
|
|
1012
|
+
console.log(" SKIP elementsFromPoint \u2014 zero dimension: width=".concat(r.width, " height=").concat(r.height));
|
|
1013
|
+
}
|
|
1014
|
+
}
|
|
1015
|
+
var result = visibility && r.bottom >= 0 && r.top <= height;
|
|
1016
|
+
if (debugHash) {
|
|
1017
|
+
console.log(" visibility=".concat(visibility, " | r.bottom(").concat(r.bottom.toFixed(1), ") >= 0? ").concat(r.bottom >= 0, " | r.top(").concat(r.top.toFixed(1), ") <= height(").concat(height, ")? ").concat(r.top <= height));
|
|
1018
|
+
console.log(" => final result: ".concat(result));
|
|
1019
|
+
console.groupEnd();
|
|
1020
|
+
}
|
|
1021
|
+
return result;
|
|
937
1022
|
};
|
|
938
1023
|
this.waitForDialogs = function () { return __awaiter(_this, void 0, void 0, function () {
|
|
939
1024
|
var isPortalCanvas;
|
|
@@ -1682,6 +1767,9 @@ var LayoutHelper = /** @class */ (function () {
|
|
|
1682
1767
|
// In case of selector collision, prefer the first inserted node
|
|
1683
1768
|
_this.hashMapAlpha[data.hashAlpha] = _this.get(data.hashAlpha) || parent;
|
|
1684
1769
|
_this.hashMapBeta[data.hashBeta] = _this.get(data.hashBeta) || parent;
|
|
1770
|
+
if (data.hashBeta === '2e36mrf7c' || data.hashBeta === '19lq10ve5') {
|
|
1771
|
+
console.log("[Layout] addToHashMap hashBeta=".concat(data.hashBeta, " | hashAlpha=").concat(data.hashAlpha, " | tag=").concat(parent === null || parent === void 0 ? void 0 : parent.tagName));
|
|
1772
|
+
}
|
|
1685
1773
|
};
|
|
1686
1774
|
this.resize = function (el, width, height) {
|
|
1687
1775
|
if (el && el.nodeType === 1 /* NodeType.ELEMENT_NODE */ && width && height) {
|
|
@@ -2310,6 +2398,11 @@ var Visualizer = /** @class */ (function () {
|
|
|
2310
2398
|
this._state = null;
|
|
2311
2399
|
this.renderTime = 0;
|
|
2312
2400
|
this.hashFoundTime = -1;
|
|
2401
|
+
this._excludeClassNames = [];
|
|
2402
|
+
this.configure = function (opts) {
|
|
2403
|
+
_this._excludeClassNames = opts.excludeClassNames || [];
|
|
2404
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2405
|
+
};
|
|
2313
2406
|
this.dom = function (event) { return __awaiter(_this, void 0, void 0, function () {
|
|
2314
2407
|
return __generator(this, function (_a) {
|
|
2315
2408
|
switch (_a.label) {
|
|
@@ -2346,25 +2439,33 @@ var Visualizer = /** @class */ (function () {
|
|
|
2346
2439
|
return false;
|
|
2347
2440
|
}
|
|
2348
2441
|
};
|
|
2349
|
-
this.html = function (decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy) {
|
|
2442
|
+
this.html = function (decoded, target, portalCanvasId, hash, useproxy, logerror, shortCircuitStrategy, signal) {
|
|
2350
2443
|
if (hash === void 0) { hash = null; }
|
|
2351
2444
|
if (shortCircuitStrategy === void 0) { shortCircuitStrategy = 0 /* ShortCircuitStrategy.None */; }
|
|
2352
2445
|
return __awaiter(_this, void 0, void 0, function () {
|
|
2353
|
-
var merged, entry, _a, domEvent, e_1;
|
|
2446
|
+
var merged, CHUNK_BUDGET_MS, chunkStart, entry, _a, domEvent, e_1;
|
|
2354
2447
|
return __generator(this, function (_b) {
|
|
2355
2448
|
switch (_b.label) {
|
|
2356
2449
|
case 0:
|
|
2357
|
-
if (!(decoded && decoded.length > 0 && target)) return [3 /*break*/,
|
|
2450
|
+
if (!(decoded && decoded.length > 0 && target)) return [3 /*break*/, 13];
|
|
2358
2451
|
_b.label = 1;
|
|
2359
2452
|
case 1:
|
|
2360
|
-
_b.trys.push([1,
|
|
2453
|
+
_b.trys.push([1, 12, , 13]);
|
|
2361
2454
|
merged = this.merge(decoded);
|
|
2455
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted)
|
|
2456
|
+
return [2 /*return*/, this];
|
|
2362
2457
|
return [4 /*yield*/, this.setup(target, { version: decoded[0].envelope.version, dom: merged.dom, useproxy: useproxy, portalCanvasId: portalCanvasId })];
|
|
2363
2458
|
case 2:
|
|
2364
2459
|
_b.sent();
|
|
2460
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted)
|
|
2461
|
+
return [2 /*return*/, this];
|
|
2462
|
+
CHUNK_BUDGET_MS = 8;
|
|
2463
|
+
chunkStart = performance.now();
|
|
2365
2464
|
_b.label = 3;
|
|
2366
2465
|
case 3:
|
|
2367
|
-
if (!(merged.events.length > 0)) return [3 /*break*/,
|
|
2466
|
+
if (!(merged.events.length > 0)) return [3 /*break*/, 11];
|
|
2467
|
+
if (signal === null || signal === void 0 ? void 0 : signal.aborted)
|
|
2468
|
+
return [2 /*return*/, this];
|
|
2368
2469
|
entry = merged.events.shift();
|
|
2369
2470
|
_a = entry.event;
|
|
2370
2471
|
switch (_a) {
|
|
@@ -2390,15 +2491,22 @@ var Visualizer = /** @class */ (function () {
|
|
|
2390
2491
|
case 7:
|
|
2391
2492
|
_b.sent();
|
|
2392
2493
|
return [3 /*break*/, 8];
|
|
2393
|
-
case 8:
|
|
2394
|
-
|
|
2395
|
-
|
|
2494
|
+
case 8:
|
|
2495
|
+
if (!(performance.now() - chunkStart >= CHUNK_BUDGET_MS)) return [3 /*break*/, 10];
|
|
2496
|
+
return [4 /*yield*/, new Promise(function (resolve) { return setTimeout(resolve, 0); })];
|
|
2497
|
+
case 9:
|
|
2498
|
+
_b.sent();
|
|
2499
|
+
chunkStart = performance.now();
|
|
2500
|
+
_b.label = 10;
|
|
2501
|
+
case 10: return [3 /*break*/, 3];
|
|
2502
|
+
case 11: return [3 /*break*/, 13];
|
|
2503
|
+
case 12:
|
|
2396
2504
|
e_1 = _b.sent();
|
|
2397
2505
|
if (logerror) {
|
|
2398
2506
|
logerror(e_1);
|
|
2399
2507
|
}
|
|
2400
|
-
return [3 /*break*/,
|
|
2401
|
-
case
|
|
2508
|
+
return [3 /*break*/, 13];
|
|
2509
|
+
case 13: return [2 /*return*/, this];
|
|
2402
2510
|
}
|
|
2403
2511
|
});
|
|
2404
2512
|
});
|
|
@@ -2430,6 +2538,7 @@ var Visualizer = /** @class */ (function () {
|
|
|
2430
2538
|
decoded = decoded.sort(_this.sortPayloads);
|
|
2431
2539
|
// Re-initialize enrich class if someone ends up calling merge function directly
|
|
2432
2540
|
_this.enrich = _this.enrich || new EnrichHelper();
|
|
2541
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2433
2542
|
_this.enrich.reset();
|
|
2434
2543
|
// Walk through payloads and generate merged payload from an array of decoded payloads
|
|
2435
2544
|
for (var _i = 0, decoded_1 = decoded; _i < decoded_1.length; _i++) {
|
|
@@ -2468,6 +2577,10 @@ var Visualizer = /** @class */ (function () {
|
|
|
2468
2577
|
switch (_a.label) {
|
|
2469
2578
|
case 0:
|
|
2470
2579
|
this.reset();
|
|
2580
|
+
if (options.excludeClassNames) {
|
|
2581
|
+
this._excludeClassNames = options.excludeClassNames;
|
|
2582
|
+
helper.selector.configure(options.excludeClassNames);
|
|
2583
|
+
}
|
|
2471
2584
|
// Infer options
|
|
2472
2585
|
options.pointer = "pointer" in options ? options.pointer : true;
|
|
2473
2586
|
options.canvas = "canvas" in options ? options.canvas : true;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gemx-dev/clarity-visualize",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.64",
|
|
4
4
|
"description": "Clarity visualize",
|
|
5
5
|
"author": "Microsoft Corp.",
|
|
6
6
|
"license": "MIT",
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
"unpkg": "build/clarity.visualize.min.js",
|
|
10
10
|
"types": "types/index.d.ts",
|
|
11
11
|
"dependencies": {
|
|
12
|
-
"@gemx-dev/clarity-decode": "^0.8.
|
|
12
|
+
"@gemx-dev/clarity-decode": "^0.8.64"
|
|
13
13
|
},
|
|
14
14
|
"devDependencies": {
|
|
15
15
|
"@rollup/plugin-commonjs": "^24.0.0",
|
package/src/heatmap.ts
CHANGED
|
@@ -276,11 +276,32 @@ export class HeatmapHelper {
|
|
|
276
276
|
let points: { [key: string]: number } = {};
|
|
277
277
|
let localMax = 0;
|
|
278
278
|
let height = this.state.window && this.state.window.document ? this.state.window.document.documentElement.clientHeight : 0;
|
|
279
|
+
|
|
280
|
+
// DEBUG LOG
|
|
281
|
+
console.group(`[Heatmap] transform — total elements: ${this.data.length}, height: ${height}, max: ${this.max}`);
|
|
282
|
+
|
|
279
283
|
for (let element of this.data) {
|
|
280
284
|
let el = this.layout.get(element.hash) as HTMLElement;
|
|
285
|
+
if (!el) {
|
|
286
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — element not found in DOM`);
|
|
287
|
+
continue;
|
|
288
|
+
}
|
|
289
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
290
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — getBoundingClientRect not available`, el);
|
|
291
|
+
continue;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
let r = el.getBoundingClientRect();
|
|
295
|
+
const isDebugHash = element.hash === "270qs0pw7";
|
|
296
|
+
let v = this.visible(el, r, height, isDebugHash ? element.hash : undefined);
|
|
297
|
+
|
|
298
|
+
if (!v && this.max !== null) {
|
|
299
|
+
console.warn(`[Heatmap] SKIP hash="${element.hash}" — not visible on re-render`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height }, maxIsSet: this.max !== null });
|
|
300
|
+
continue;
|
|
301
|
+
}
|
|
302
|
+
|
|
281
303
|
if (el && typeof el.getBoundingClientRect === "function") {
|
|
282
|
-
|
|
283
|
-
let v = this.visible(el, r, height);
|
|
304
|
+
console.log(`[Heatmap] PROCESS hash="${element.hash}" visible=${v} points=${element.points}`, { rect: { top: r.top, left: r.left, width: r.width, height: r.height } });
|
|
284
305
|
// Process clicks for only visible elements
|
|
285
306
|
if (this.max === null || v) {
|
|
286
307
|
for(let i = 0; i < element.points; i++) {
|
|
@@ -289,6 +310,7 @@ export class HeatmapHelper {
|
|
|
289
310
|
let k = `${x}${Constant.Separator}${y}${Constant.Separator}${v ? 1 : 0}`;
|
|
290
311
|
points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
|
|
291
312
|
localMax = Math.max(points[k], localMax);
|
|
313
|
+
console.log(` point[${i}] x=${x} y=${y} visible=${v} clicks=${element.clicks[i]}`);
|
|
292
314
|
}
|
|
293
315
|
}
|
|
294
316
|
}
|
|
@@ -298,23 +320,49 @@ export class HeatmapHelper {
|
|
|
298
320
|
this.max = this.max ? this.max : localMax;
|
|
299
321
|
|
|
300
322
|
// Once all points are accounted for, convert everything into absolute (x, y)
|
|
323
|
+
let skippedInvisible = 0;
|
|
301
324
|
for (let coordinates of Object.keys(points)) {
|
|
302
325
|
let parts = coordinates.split(Constant.Separator);
|
|
303
326
|
let alpha = Math.min((points[coordinates] / this.max) + Setting.AlphaBoost, 1);
|
|
304
|
-
if (parts[2] === "1") {
|
|
327
|
+
if (parts[2] === "1") {
|
|
328
|
+
output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
|
|
329
|
+
} else {
|
|
330
|
+
skippedInvisible++;
|
|
331
|
+
}
|
|
305
332
|
}
|
|
306
333
|
|
|
334
|
+
console.log(`[Heatmap] Result — drawn: ${output.length}, skipped invisible: ${skippedInvisible}, max: ${this.max}`);
|
|
335
|
+
console.groupEnd();
|
|
336
|
+
|
|
307
337
|
return output;
|
|
308
338
|
}
|
|
309
339
|
|
|
310
|
-
private visible = (el: HTMLElement, r: DOMRect, height: number): boolean => {
|
|
340
|
+
private visible = (el: HTMLElement, r: DOMRect, height: number, debugHash?: string): boolean => {
|
|
311
341
|
let doc: Document | ShadowRoot = this.state.window.document;
|
|
312
342
|
let visibility = r.height > height ? true : false;
|
|
343
|
+
|
|
344
|
+
if (debugHash) {
|
|
345
|
+
console.group(`[Heatmap visible] hash=${debugHash} tag=${el?.tagName}`);
|
|
346
|
+
console.log(` rect: top=${r.top.toFixed(1)} bottom=${r.bottom.toFixed(1)} left=${r.left.toFixed(1)} width=${r.width.toFixed(1)} height=${r.height.toFixed(1)}`);
|
|
347
|
+
console.log(` viewport height=${height} | r.height > height? ${r.height > height}`);
|
|
348
|
+
}
|
|
349
|
+
|
|
313
350
|
if (visibility === false && r.width > 0 && r.height > 0) {
|
|
351
|
+
const checkX = r.left + (r.width / 2);
|
|
352
|
+
const checkY = r.top + (r.height / 2);
|
|
353
|
+
if (debugHash) { console.log(` elementsFromPoint(${checkX.toFixed(1)}, ${checkY.toFixed(1)})`); }
|
|
354
|
+
|
|
314
355
|
while (!visibility && doc)
|
|
315
356
|
{
|
|
316
357
|
let shadowElement = null;
|
|
317
|
-
let elements = doc.elementsFromPoint(
|
|
358
|
+
let elements = doc.elementsFromPoint(checkX, checkY);
|
|
359
|
+
|
|
360
|
+
if (debugHash) {
|
|
361
|
+
const topElements = elements.slice(0, 5).map(e => `${e.tagName}${e.id ? '#' + e.id : ''}${e.className ? '.' + String(e.className).trim().split(/\s+/).slice(0, 2).join('.') : ''}`);
|
|
362
|
+
console.log(` top elements at center: [${topElements.join(', ')}]`);
|
|
363
|
+
console.log(` is el in top elements? ${elements.includes(el)} | el matches first non-canvas? ${elements.find(e => !(e.tagName === Constant.Canvas || (e.id && e.id.indexOf(Constant.ClarityPrefix) === 0))) === el}`);
|
|
364
|
+
}
|
|
365
|
+
|
|
318
366
|
for (let e of elements) {
|
|
319
367
|
// Ignore if top element ends up being the canvas element we added for heatmap visualization
|
|
320
368
|
if (e.tagName === Constant.Canvas || (e.id && e.id.indexOf(Constant.ClarityPrefix) === 0)) { continue; }
|
|
@@ -324,8 +372,19 @@ export class HeatmapHelper {
|
|
|
324
372
|
}
|
|
325
373
|
doc = shadowElement;
|
|
326
374
|
}
|
|
375
|
+
} else if (debugHash) {
|
|
376
|
+
if (r.width === 0 || r.height === 0) {
|
|
377
|
+
console.log(` SKIP elementsFromPoint — zero dimension: width=${r.width} height=${r.height}`);
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const result = visibility && r.bottom >= 0 && r.top <= height;
|
|
382
|
+
if (debugHash) {
|
|
383
|
+
console.log(` visibility=${visibility} | r.bottom(${r.bottom.toFixed(1)}) >= 0? ${r.bottom >= 0} | r.top(${r.top.toFixed(1)}) <= height(${height})? ${r.top <= height}`);
|
|
384
|
+
console.log(` => final result: ${result}`);
|
|
385
|
+
console.groupEnd();
|
|
327
386
|
}
|
|
328
|
-
return
|
|
387
|
+
return result;
|
|
329
388
|
}
|
|
330
389
|
|
|
331
390
|
private waitForDialogs = async (): Promise<void> => {
|