@gemx-dev/clarity-visualize 0.8.61 → 0.8.63
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 +153 -102
- package/build/clarity.visualize.min.js +1 -1
- package/build/clarity.visualize.module.js +153 -102
- package/package.json +2 -2
- package/src/heatmap.ts +33 -3
- package/src/visualizer.ts +14 -2
- package/types/visualize.d.ts +2 -0
|
@@ -69,105 +69,6 @@ function __generator(thisArg, body) {
|
|
|
69
69
|
}
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
-
var _a$1;
|
|
73
|
-
var DataHelper = /** @class */ (function () {
|
|
74
|
-
function DataHelper(state) {
|
|
75
|
-
var _this = this;
|
|
76
|
-
this.regionMap = {};
|
|
77
|
-
this.regions = {};
|
|
78
|
-
this.metrics = {};
|
|
79
|
-
this.lean = false;
|
|
80
|
-
this.reset = function () {
|
|
81
|
-
_this.metrics = {};
|
|
82
|
-
_this.lean = false;
|
|
83
|
-
_this.regions = {};
|
|
84
|
-
_this.regionMap = {};
|
|
85
|
-
};
|
|
86
|
-
this.metric = function (event) {
|
|
87
|
-
var _a;
|
|
88
|
-
if (_this.state.options.metadata) {
|
|
89
|
-
var metricMarkup = [];
|
|
90
|
-
var regionMarkup = [];
|
|
91
|
-
// Copy over metrics for future reference
|
|
92
|
-
for (var m in event.data) {
|
|
93
|
-
var eventType = typeof event.data[m];
|
|
94
|
-
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
95
|
-
if (!(m in _this.metrics)) {
|
|
96
|
-
_this.metrics[m] = 0;
|
|
97
|
-
}
|
|
98
|
-
var key = parseInt(m, 10);
|
|
99
|
-
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
100
|
-
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
101
|
-
_this.metrics[m] = value;
|
|
102
|
-
}
|
|
103
|
-
else {
|
|
104
|
-
_this.metrics[m] += value;
|
|
105
|
-
}
|
|
106
|
-
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
for (var entry in _this.metrics) {
|
|
110
|
-
if (entry in DataHelper.METRIC_MAP) {
|
|
111
|
-
var m = _this.metrics[entry];
|
|
112
|
-
var map = DataHelper.METRIC_MAP[entry];
|
|
113
|
-
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
114
|
-
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// Append region information to metadata
|
|
118
|
-
for (var name_1 in _this.regions) {
|
|
119
|
-
var r = _this.regions[name_1];
|
|
120
|
-
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
121
|
-
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
122
|
-
}
|
|
123
|
-
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
124
|
-
}
|
|
125
|
-
};
|
|
126
|
-
this.key = function (unit) {
|
|
127
|
-
switch (unit) {
|
|
128
|
-
case "html-price":
|
|
129
|
-
case "ld-price":
|
|
130
|
-
case "cls":
|
|
131
|
-
return "" /* Data.Constant.Empty */;
|
|
132
|
-
default: return unit;
|
|
133
|
-
}
|
|
134
|
-
};
|
|
135
|
-
this.value = function (num, unit) {
|
|
136
|
-
switch (unit) {
|
|
137
|
-
case "KB": return Math.round(num / 1024);
|
|
138
|
-
case "s": return Math.round(num / 10) / 100;
|
|
139
|
-
case "cls": return num / 1000;
|
|
140
|
-
case "html-price": return num / 100;
|
|
141
|
-
default: return num;
|
|
142
|
-
}
|
|
143
|
-
};
|
|
144
|
-
this.state = state;
|
|
145
|
-
}
|
|
146
|
-
DataHelper.prototype.region = function (event) {
|
|
147
|
-
var data = event.data;
|
|
148
|
-
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
149
|
-
var r = data_1[_i];
|
|
150
|
-
if (!(r.name in this.regions)) {
|
|
151
|
-
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
152
|
-
}
|
|
153
|
-
this.regionMap[r.id] = r.name;
|
|
154
|
-
}
|
|
155
|
-
};
|
|
156
|
-
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
157
|
-
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
158
|
-
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
159
|
-
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
160
|
-
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
161
|
-
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
162
|
-
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
163
|
-
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
164
|
-
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
165
|
-
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
166
|
-
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
167
|
-
_a$1);
|
|
168
|
-
return DataHelper;
|
|
169
|
-
}());
|
|
170
|
-
|
|
171
72
|
// tslint:disable: no-bitwise
|
|
172
73
|
function hash (input, precision) {
|
|
173
74
|
if (precision === void 0) { precision = null; }
|
|
@@ -188,12 +89,26 @@ function hash (input, precision) {
|
|
|
188
89
|
hash = Math.abs(hashOne + (hashTwo * 11579));
|
|
189
90
|
return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
|
|
190
91
|
}
|
|
92
|
+
|
|
93
|
+
function __spreadArray(to, from, pack) {
|
|
94
|
+
if (pack || arguments.length === 2) for (var i = 0, l = from.length, ar; i < l; i++) {
|
|
95
|
+
if (ar || !(i in from)) {
|
|
96
|
+
if (!ar) ar = Array.prototype.slice.call(from, 0, i);
|
|
97
|
+
ar[i] = from[i];
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
return to.concat(ar || Array.prototype.slice.call(from));
|
|
101
|
+
}
|
|
191
102
|
|
|
192
103
|
var excludeClassNames = "load,active,fixed,visible,focus,show,collaps,animat" /* Constant.ExcludeClassNames */.split("," /* Constant.Comma */);
|
|
104
|
+
var extraExcludeClassNames = [];
|
|
193
105
|
var selectorMap = {};
|
|
194
106
|
function reset$8() {
|
|
195
107
|
selectorMap = {};
|
|
196
108
|
}
|
|
109
|
+
function configure(classNames) {
|
|
110
|
+
extraExcludeClassNames = classNames || [];
|
|
111
|
+
}
|
|
197
112
|
function get$1(input, type) {
|
|
198
113
|
var a = input.attributes;
|
|
199
114
|
var prefix = input.prefix ? input.prefix[type] : null;
|
|
@@ -265,7 +180,7 @@ function filter(value) {
|
|
|
265
180
|
if (!value) {
|
|
266
181
|
return false;
|
|
267
182
|
} // Do not process empty strings
|
|
268
|
-
if (
|
|
183
|
+
if (getExcludeClassNames().some(function (x) { return value.toLowerCase().indexOf(x) >= 0; })) {
|
|
269
184
|
return false;
|
|
270
185
|
}
|
|
271
186
|
for (var i = 0; i < value.length; i++) {
|
|
@@ -276,9 +191,13 @@ function filter(value) {
|
|
|
276
191
|
}
|
|
277
192
|
return true;
|
|
278
193
|
}
|
|
194
|
+
function getExcludeClassNames() {
|
|
195
|
+
return __spreadArray(__spreadArray([], excludeClassNames, true), extraExcludeClassNames, true);
|
|
196
|
+
}
|
|
279
197
|
|
|
280
198
|
var selector = /*#__PURE__*/Object.freeze({
|
|
281
199
|
__proto__: null,
|
|
200
|
+
configure: configure,
|
|
282
201
|
get: get$1,
|
|
283
202
|
reset: reset$8
|
|
284
203
|
});
|
|
@@ -314,6 +233,105 @@ function lookup(hash) {
|
|
|
314
233
|
|
|
315
234
|
var helper = { hash: hash, selector: selector, get: get$2, getNode: getNode, lookup: lookup };
|
|
316
235
|
|
|
236
|
+
var _a$1;
|
|
237
|
+
var DataHelper = /** @class */ (function () {
|
|
238
|
+
function DataHelper(state) {
|
|
239
|
+
var _this = this;
|
|
240
|
+
this.regionMap = {};
|
|
241
|
+
this.regions = {};
|
|
242
|
+
this.metrics = {};
|
|
243
|
+
this.lean = false;
|
|
244
|
+
this.reset = function () {
|
|
245
|
+
_this.metrics = {};
|
|
246
|
+
_this.lean = false;
|
|
247
|
+
_this.regions = {};
|
|
248
|
+
_this.regionMap = {};
|
|
249
|
+
};
|
|
250
|
+
this.metric = function (event) {
|
|
251
|
+
var _a;
|
|
252
|
+
if (_this.state.options.metadata) {
|
|
253
|
+
var metricMarkup = [];
|
|
254
|
+
var regionMarkup = [];
|
|
255
|
+
// Copy over metrics for future reference
|
|
256
|
+
for (var m in event.data) {
|
|
257
|
+
var eventType = typeof event.data[m];
|
|
258
|
+
if (eventType === "number" || (event.event === 1 /* Data.Event.Dimension */ && m === 37 /* Data.Dimension.InteractionNextPaint */.toString())) {
|
|
259
|
+
if (!(m in _this.metrics)) {
|
|
260
|
+
_this.metrics[m] = 0;
|
|
261
|
+
}
|
|
262
|
+
var key = parseInt(m, 10);
|
|
263
|
+
var value = eventType === "object" ? Number((_a = event.data[m]) === null || _a === void 0 ? void 0 : _a[0]) : event.data[m];
|
|
264
|
+
if (m in DataHelper.METRIC_MAP && (DataHelper.METRIC_MAP[m].unit === "html-price" || DataHelper.METRIC_MAP[m].unit === "ld-price")) {
|
|
265
|
+
_this.metrics[m] = value;
|
|
266
|
+
}
|
|
267
|
+
else {
|
|
268
|
+
_this.metrics[m] += value;
|
|
269
|
+
}
|
|
270
|
+
_this.lean = key === 1 /* Data.Metric.Playback */ && value === 0 ? true : _this.lean;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
for (var entry in _this.metrics) {
|
|
274
|
+
if (entry in DataHelper.METRIC_MAP) {
|
|
275
|
+
var m = _this.metrics[entry];
|
|
276
|
+
var map = DataHelper.METRIC_MAP[entry];
|
|
277
|
+
var unit = "unit" in map ? map.unit : "" /* Data.Constant.Empty */;
|
|
278
|
+
metricMarkup.push("<li><h2>".concat(_this.value(m, unit), "<span>").concat(_this.key(unit), "</span></h2>").concat(map.name, "</li>"));
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
// Append region information to metadata
|
|
282
|
+
for (var name_1 in _this.regions) {
|
|
283
|
+
var r = _this.regions[name_1];
|
|
284
|
+
var className = (r.visibility === 10 /* Layout.RegionVisibility.Visible */ ? "visible" : (r.interaction === 20 /* Layout.InteractionState.Clicked */ ? "clicked" : "" /* Data.Constant.Empty */));
|
|
285
|
+
regionMarkup.push("<span class=\"".concat(className, "\">").concat(name_1, "</span>"));
|
|
286
|
+
}
|
|
287
|
+
_this.state.options.metadata.innerHTML = "<ul>".concat(metricMarkup.join("" /* Data.Constant.Empty */), "</ul><div>").concat(regionMarkup.join("" /* Data.Constant.Empty */), "</div>");
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
this.key = function (unit) {
|
|
291
|
+
switch (unit) {
|
|
292
|
+
case "html-price":
|
|
293
|
+
case "ld-price":
|
|
294
|
+
case "cls":
|
|
295
|
+
return "" /* Data.Constant.Empty */;
|
|
296
|
+
default: return unit;
|
|
297
|
+
}
|
|
298
|
+
};
|
|
299
|
+
this.value = function (num, unit) {
|
|
300
|
+
switch (unit) {
|
|
301
|
+
case "KB": return Math.round(num / 1024);
|
|
302
|
+
case "s": return Math.round(num / 10) / 100;
|
|
303
|
+
case "cls": return num / 1000;
|
|
304
|
+
case "html-price": return num / 100;
|
|
305
|
+
default: return num;
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
this.state = state;
|
|
309
|
+
}
|
|
310
|
+
DataHelper.prototype.region = function (event) {
|
|
311
|
+
var data = event.data;
|
|
312
|
+
for (var _i = 0, data_1 = data; _i < data_1.length; _i++) {
|
|
313
|
+
var r = data_1[_i];
|
|
314
|
+
if (!(r.name in this.regions)) {
|
|
315
|
+
this.regions[r.name] = { interaction: r.interaction, visibility: r.visibility };
|
|
316
|
+
}
|
|
317
|
+
this.regionMap[r.id] = r.name;
|
|
318
|
+
}
|
|
319
|
+
};
|
|
320
|
+
DataHelper.METRIC_MAP = (_a$1 = {},
|
|
321
|
+
_a$1[2 /* Data.Metric.TotalBytes */] = { name: "Total Bytes", unit: "KB" },
|
|
322
|
+
_a$1[4 /* Data.Metric.TotalCost */] = { name: "Total Cost", unit: "ms" },
|
|
323
|
+
_a$1[3 /* Data.Metric.LayoutCost */] = { name: "Layout Cost", unit: "ms" },
|
|
324
|
+
_a$1[8 /* Data.Metric.LargestPaint */] = { name: "LCP", unit: "s" },
|
|
325
|
+
_a$1[9 /* Data.Metric.CumulativeLayoutShift */] = { name: "CLS", unit: "cls" },
|
|
326
|
+
_a$1[7 /* Data.Metric.LongTaskCount */] = { name: "Long Tasks" },
|
|
327
|
+
_a$1[24 /* Data.Metric.CartTotal */] = { name: "Cart Total", unit: "html-price" },
|
|
328
|
+
_a$1[13 /* Data.Metric.ProductPrice */] = { name: "Product Price", unit: "ld-price" },
|
|
329
|
+
_a$1[6 /* Data.Metric.ThreadBlockedTime */] = { name: "Thread Blocked", unit: "ms" },
|
|
330
|
+
_a$1[37 /* Data.Dimension.InteractionNextPaint */] = { name: "INP", unit: "ms" },
|
|
331
|
+
_a$1);
|
|
332
|
+
return DataHelper;
|
|
333
|
+
}());
|
|
334
|
+
|
|
317
335
|
var EnrichHelper = /** @class */ (function () {
|
|
318
336
|
function EnrichHelper() {
|
|
319
337
|
var _this = this;
|
|
@@ -807,6 +825,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
807
825
|
if (canvas === null) {
|
|
808
826
|
canvas = doc.createElement("CANVAS" /* Constant.Canvas */);
|
|
809
827
|
canvas.id = "clarity-heatmap-canvas" /* Constant.HeatmapCanvas */;
|
|
828
|
+
canvas.classList.add("clarity-heatmap-canvas" /* Constant.HeatmapCanvas */);
|
|
810
829
|
canvas.width = 0;
|
|
811
830
|
canvas.height = 0;
|
|
812
831
|
canvas.style.position = "absolute" /* Constant.Absolute */;
|
|
@@ -883,12 +902,27 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
883
902
|
var points = {};
|
|
884
903
|
var localMax = 0;
|
|
885
904
|
var height = _this.state.window && _this.state.window.document ? _this.state.window.document.documentElement.clientHeight : 0;
|
|
905
|
+
// DEBUG LOG
|
|
906
|
+
console.group("[Heatmap] transform \u2014 total elements: ".concat(_this.data.length, ", height: ").concat(height, ", max: ").concat(_this.max));
|
|
886
907
|
for (var _i = 0, _a = _this.data; _i < _a.length; _i++) {
|
|
887
908
|
var element = _a[_i];
|
|
888
909
|
var el = _this.layout.get(element.hash);
|
|
910
|
+
if (!el) {
|
|
911
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 element not found in DOM"));
|
|
912
|
+
continue;
|
|
913
|
+
}
|
|
914
|
+
if (typeof el.getBoundingClientRect !== "function") {
|
|
915
|
+
console.warn("[Heatmap] SKIP hash=\"".concat(element.hash, "\" \u2014 getBoundingClientRect not available"), el);
|
|
916
|
+
continue;
|
|
917
|
+
}
|
|
918
|
+
var r = el.getBoundingClientRect();
|
|
919
|
+
var v = _this.visible(el, r, height);
|
|
920
|
+
if (!v && _this.max !== null) {
|
|
921
|
+
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 });
|
|
922
|
+
continue;
|
|
923
|
+
}
|
|
889
924
|
if (el && typeof el.getBoundingClientRect === "function") {
|
|
890
|
-
|
|
891
|
-
var v = _this.visible(el, r, height);
|
|
925
|
+
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 } });
|
|
892
926
|
// Process clicks for only visible elements
|
|
893
927
|
if (_this.max === null || v) {
|
|
894
928
|
for (var i = 0; i < element.points; i++) {
|
|
@@ -897,6 +931,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
897
931
|
var k = "".concat(x).concat("X" /* Constant.Separator */).concat(y).concat("X" /* Constant.Separator */).concat(v ? 1 : 0);
|
|
898
932
|
points[k] = k in points ? points[k] + element.clicks[i] : element.clicks[i];
|
|
899
933
|
localMax = Math.max(points[k], localMax);
|
|
934
|
+
console.log(" point[".concat(i, "] x=").concat(x, " y=").concat(y, " visible=").concat(v, " clicks=").concat(element.clicks[i]));
|
|
900
935
|
}
|
|
901
936
|
}
|
|
902
937
|
}
|
|
@@ -904,6 +939,7 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
904
939
|
// Set the max value from the firs t
|
|
905
940
|
_this.max = _this.max ? _this.max : localMax;
|
|
906
941
|
// Once all points are accounted for, convert everything into absolute (x, y)
|
|
942
|
+
var skippedInvisible = 0;
|
|
907
943
|
for (var _b = 0, _c = Object.keys(points); _b < _c.length; _b++) {
|
|
908
944
|
var coordinates = _c[_b];
|
|
909
945
|
var parts = coordinates.split("X" /* Constant.Separator */);
|
|
@@ -911,7 +947,12 @@ var HeatmapHelper = /** @class */ (function () {
|
|
|
911
947
|
if (parts[2] === "1") {
|
|
912
948
|
output.push({ x: parseInt(parts[0], 10), y: parseInt(parts[1], 10), a: alpha });
|
|
913
949
|
}
|
|
950
|
+
else {
|
|
951
|
+
skippedInvisible++;
|
|
952
|
+
}
|
|
914
953
|
}
|
|
954
|
+
console.log("[Heatmap] Result \u2014 drawn: ".concat(output.length, ", skipped invisible: ").concat(skippedInvisible, ", max: ").concat(_this.max));
|
|
955
|
+
console.groupEnd();
|
|
915
956
|
return output;
|
|
916
957
|
};
|
|
917
958
|
this.visible = function (el, r, height) {
|
|
@@ -2311,6 +2352,11 @@ var Visualizer = /** @class */ (function () {
|
|
|
2311
2352
|
this._state = null;
|
|
2312
2353
|
this.renderTime = 0;
|
|
2313
2354
|
this.hashFoundTime = -1;
|
|
2355
|
+
this._excludeClassNames = [];
|
|
2356
|
+
this.configure = function (opts) {
|
|
2357
|
+
_this._excludeClassNames = opts.excludeClassNames || [];
|
|
2358
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2359
|
+
};
|
|
2314
2360
|
this.dom = function (event) { return __awaiter(_this, void 0, void 0, function () {
|
|
2315
2361
|
return __generator(this, function (_a) {
|
|
2316
2362
|
switch (_a.label) {
|
|
@@ -2431,6 +2477,7 @@ var Visualizer = /** @class */ (function () {
|
|
|
2431
2477
|
decoded = decoded.sort(_this.sortPayloads);
|
|
2432
2478
|
// Re-initialize enrich class if someone ends up calling merge function directly
|
|
2433
2479
|
_this.enrich = _this.enrich || new EnrichHelper();
|
|
2480
|
+
helper.selector.configure(_this._excludeClassNames);
|
|
2434
2481
|
_this.enrich.reset();
|
|
2435
2482
|
// Walk through payloads and generate merged payload from an array of decoded payloads
|
|
2436
2483
|
for (var _i = 0, decoded_1 = decoded; _i < decoded_1.length; _i++) {
|
|
@@ -2469,6 +2516,10 @@ var Visualizer = /** @class */ (function () {
|
|
|
2469
2516
|
switch (_a.label) {
|
|
2470
2517
|
case 0:
|
|
2471
2518
|
this.reset();
|
|
2519
|
+
if (options.excludeClassNames) {
|
|
2520
|
+
this._excludeClassNames = options.excludeClassNames;
|
|
2521
|
+
helper.selector.configure(options.excludeClassNames);
|
|
2522
|
+
}
|
|
2472
2523
|
// Infer options
|
|
2473
2524
|
options.pointer = "pointer" in options ? options.pointer : true;
|
|
2474
2525
|
options.canvas = "canvas" in options ? options.canvas : true;
|