@sailfish-ai/recorder 1.12.7 → 1.12.8

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.
Files changed (61) hide show
  1. package/dist/chunks/{canvasManager-B9uQ-PFS.js → canvasManager-3AzRUaNq.js} +1 -1
  2. package/dist/chunks/canvasManager-3AzRUaNq.js.br +0 -0
  3. package/dist/chunks/canvasManager-3AzRUaNq.js.gz +0 -0
  4. package/dist/chunks/{canvasManager-Ce7vazTt.js → canvasManager-CDEhVz_J.js} +1 -1
  5. package/dist/chunks/canvasManager-CDEhVz_J.js.br +0 -0
  6. package/dist/chunks/canvasManager-CDEhVz_J.js.gz +0 -0
  7. package/dist/chunks/{chunkSerializer-B-z7p0r-.js → chunkSerializer-B3loCr3q.js} +1 -1
  8. package/dist/chunks/chunkSerializer-B3loCr3q.js.br +0 -0
  9. package/dist/chunks/chunkSerializer-B3loCr3q.js.gz +0 -0
  10. package/dist/chunks/{chunkSerializer-C1knjCQ5.js → chunkSerializer-Cz0TPV-B.js} +1 -1
  11. package/dist/chunks/chunkSerializer-Cz0TPV-B.js.br +0 -0
  12. package/dist/chunks/chunkSerializer-Cz0TPV-B.js.gz +0 -0
  13. package/dist/chunks/hoverDetection-Bl-yKKI3.js +137 -0
  14. package/dist/chunks/hoverDetection-Bl-yKKI3.js.br +0 -0
  15. package/dist/chunks/hoverDetection-Bl-yKKI3.js.gz +0 -0
  16. package/dist/chunks/hoverDetection-CYsyBQjj.js +139 -0
  17. package/dist/chunks/hoverDetection-CYsyBQjj.js.br +0 -0
  18. package/dist/chunks/hoverDetection-CYsyBQjj.js.gz +0 -0
  19. package/dist/chunks/{index-BMHQ-0NY.js → index-BUAmLDnY.js} +109 -103
  20. package/dist/chunks/index-BUAmLDnY.js.br +0 -0
  21. package/dist/chunks/index-BUAmLDnY.js.gz +0 -0
  22. package/dist/chunks/{index-qgM1hFXO.js → index-CPZwt1ko.js} +59 -53
  23. package/dist/chunks/index-CPZwt1ko.js.br +0 -0
  24. package/dist/chunks/index-CPZwt1ko.js.gz +0 -0
  25. package/dist/hoverDetection.js +371 -0
  26. package/dist/hoverDetection.js.br +0 -0
  27. package/dist/hoverDetection.js.gz +0 -0
  28. package/dist/hoverDetection.test.js +187 -0
  29. package/dist/hoverDetection.test.js.br +0 -0
  30. package/dist/hoverDetection.test.js.gz +0 -0
  31. package/dist/index.js +8 -0
  32. package/dist/index.js.br +0 -0
  33. package/dist/index.js.gz +0 -0
  34. package/dist/recorder.cjs +1 -1
  35. package/dist/recorder.cjs.br +0 -0
  36. package/dist/recorder.cjs.gz +0 -0
  37. package/dist/recorder.js +1 -1
  38. package/dist/recorder.js.br +0 -0
  39. package/dist/recorder.js.gz +0 -0
  40. package/dist/recorder.umd.cjs +572 -434
  41. package/dist/recorder.umd.cjs.br +0 -0
  42. package/dist/recorder.umd.cjs.gz +0 -0
  43. package/dist/recording.js +27 -0
  44. package/dist/recording.js.br +0 -0
  45. package/dist/recording.js.gz +0 -0
  46. package/dist/types/hoverDetection.d.ts +37 -0
  47. package/dist/types/hoverDetection.test.d.ts +1 -0
  48. package/dist/types/types.d.ts +6 -0
  49. package/package.json +1 -1
  50. package/dist/chunks/canvasManager-B9uQ-PFS.js.br +0 -0
  51. package/dist/chunks/canvasManager-B9uQ-PFS.js.gz +0 -0
  52. package/dist/chunks/canvasManager-Ce7vazTt.js.br +0 -0
  53. package/dist/chunks/canvasManager-Ce7vazTt.js.gz +0 -0
  54. package/dist/chunks/chunkSerializer-B-z7p0r-.js.br +0 -0
  55. package/dist/chunks/chunkSerializer-B-z7p0r-.js.gz +0 -0
  56. package/dist/chunks/chunkSerializer-C1knjCQ5.js.br +0 -0
  57. package/dist/chunks/chunkSerializer-C1knjCQ5.js.gz +0 -0
  58. package/dist/chunks/index-BMHQ-0NY.js.br +0 -0
  59. package/dist/chunks/index-BMHQ-0NY.js.gz +0 -0
  60. package/dist/chunks/index-qgM1hFXO.js.br +0 -0
  61. package/dist/chunks/index-qgM1hFXO.js.gz +0 -0
@@ -1,7 +1,7 @@
1
1
  var __defProp = Object.defineProperty;
2
2
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
3
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
- import { i as e, u as t } from "./index-BMHQ-0NY.js";
4
+ import { i as e, u as t } from "./index-BUAmLDnY.js";
5
5
  import { k as n, R as r, B as o, o as s, d as a, D as i, q as c, s as d, T as l, i as u, t as f, u as h, v as p, w as m, x as v, y, C as g, z as w, A as k, E as b, F as C, G as x, H as S, I as R, J as L, K as M, L as I, M as T, a as E } from "./patch2d-PQxcaE6r.js";
6
6
  function createBlobWorker(e2) {
7
7
  const t2 = new Blob([e2], { type: "application/javascript" }), n2 = URL.createObjectURL(t2), r2 = new Worker(n2);
@@ -3,7 +3,7 @@ var __defProp = Object.defineProperty;
3
3
  var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
4
  var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
5
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
- const e = require("./index-qgM1hFXO.js"), t = require("./patch2d-9voowhJ_.js");
6
+ const e = require("./index-CPZwt1ko.js"), t = require("./patch2d-9voowhJ_.js");
7
7
  function createBlobWorker(e2) {
8
8
  const t2 = new Blob([e2], { type: "application/javascript" }), n2 = URL.createObjectURL(t2), r2 = new Worker(n2);
9
9
  return URL.revokeObjectURL(n2), r2;
@@ -1,4 +1,4 @@
1
- import { y as e } from "./index-BMHQ-0NY.js";
1
+ import { y as e } from "./index-BUAmLDnY.js";
2
2
  async function chunkedSnapshot(t, n, s = {}) {
3
3
  const o = s.chunkSize ?? 500, r = s.maxChunkMs ?? 16, { blockClass: a, blockSelector: c, maskTextClass: i, maskTextSelector: u, maskInputSelector: d, maskInputOptions: l, maskInputFn: f, maskTextFn: N, unmaskSelector: m } = s;
4
4
  function matchesSelectorSafe(e2, t2) {
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
3
- const e = require("./index-qgM1hFXO.js");
3
+ const e = require("./index-CPZwt1ko.js");
4
4
  exports.chunkedSnapshot = async function chunkedSnapshot(t, n, s = {}) {
5
5
  const o = s.chunkSize ?? 500, r = s.maxChunkMs ?? 16, { blockClass: a, blockSelector: c, maskTextClass: i, maskTextSelector: u, maskInputSelector: d, maskInputOptions: l, maskInputFn: f, maskTextFn: N, unmaskSelector: h } = s;
6
6
  function matchesSelectorSafe(e2, t2) {
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
4
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
5
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
6
+ const e = require("@sailfish-rrweb/types"), t = /* @__PURE__ */ new Set(["menu", "menuitem", "menubar", "tooltip"]);
7
+ let n = -1, o = [];
8
+ function collectHoverBases(e2, t2) {
9
+ for (const n2 of e2.split(",")) if (-1 !== n2.indexOf(":hover")) for (const e3 of n2.split(/\s*[>+~]\s*|\s+/)) {
10
+ if (-1 === e3.indexOf(":hover")) continue;
11
+ const n3 = e3.replace(/:hover\b/g, "").replace(/::[a-zA-Z-]+/g, "").trim();
12
+ n3 && t2.push(n3);
13
+ }
14
+ }
15
+ function collectFromRules(e2, t2, n2) {
16
+ if (!(n2 > 10)) for (let o2 = 0; o2 < e2.length; o2++) {
17
+ const r2 = e2[o2], i = r2.selectorText;
18
+ i && -1 !== i.indexOf(":hover") && collectHoverBases(i, t2);
19
+ let s = null;
20
+ try {
21
+ s = r2.cssRules ?? null;
22
+ } catch {
23
+ s = null;
24
+ }
25
+ s && s.length && collectFromRules(s, t2, n2 + 1);
26
+ }
27
+ }
28
+ function matchesHoverRule(e2) {
29
+ const t2 = (function getHoverBaseSelectors() {
30
+ const e3 = document.styleSheets;
31
+ if (e3.length === n) return o;
32
+ n = e3.length;
33
+ const t3 = [];
34
+ for (let n2 = 0; n2 < e3.length; n2++) {
35
+ let o2 = null;
36
+ try {
37
+ o2 = e3[n2].cssRules;
38
+ } catch {
39
+ continue;
40
+ }
41
+ o2 && collectFromRules(o2, t3, 0);
42
+ }
43
+ return o = t3, t3;
44
+ })();
45
+ for (let n2 = 0; n2 < t2.length; n2++) try {
46
+ if (e2.matches(t2[n2])) return true;
47
+ } catch {
48
+ }
49
+ return false;
50
+ }
51
+ function hasHoverAffordance(e2, n2) {
52
+ if (e2.hasAttribute("title") || e2.hasAttribute("data-tooltip") || e2.hasAttribute("aria-describedby")) return true;
53
+ const o2 = e2.getAttribute("role");
54
+ if (e2.hasAttribute("aria-haspopup") || o2 && t.has(o2)) return true;
55
+ if (matchesHoverRule(e2)) return true;
56
+ if (!n2) try {
57
+ if ("function" == typeof window.getComputedStyle && "pointer" === window.getComputedStyle(e2).cursor) return true;
58
+ } catch {
59
+ }
60
+ return false;
61
+ }
62
+ class TokenBucket {
63
+ constructor(e2, t2, n2) {
64
+ __publicField(this, "tokens");
65
+ __publicField(this, "last");
66
+ this.capacity = e2, this.refillPerSec = t2, this.tokens = e2, this.last = n2;
67
+ }
68
+ tryConsume(e2) {
69
+ return this.tokens = Math.min(this.capacity, this.tokens + (e2 - this.last) / 1e3 * this.refillPerSec), this.last = e2, !(this.tokens < 1) && (this.tokens -= 1, true);
70
+ }
71
+ }
72
+ let r = null;
73
+ exports.installHoverDetection = function installHoverDetection(t2, n2) {
74
+ if ("undefined" == typeof window || "undefined" == typeof document) return () => {
75
+ };
76
+ r && (r(), r = null);
77
+ const o2 = Math.max(0, n2.dwellMs), i = Math.max(0, n2.cooldownMs ?? 3e3), s = n2.maxPerSession ?? 300, l = n2.maxPerSecond ?? 1, c = n2.maxDwellMs ?? 6e4, u = n2.maxAncestorDepth ?? 5, a = n2.strictAffordance ?? true, now = () => Date.now(), d = /* @__PURE__ */ new Map();
78
+ let h = 0;
79
+ const f = new TokenBucket(Math.max(10, l), l, now());
80
+ let m = null, v = 0, p = false, g = null, y = null;
81
+ const reset = () => {
82
+ null !== g && (clearTimeout(g), g = null), null !== y && (clearTimeout(y), y = null), m = null, p = false;
83
+ }, emit = (n3, o3, r2) => {
84
+ var _a, _b, _c;
85
+ const l2 = ((_b = (_a = t2.mirror) == null ? void 0 : _a.getId) == null ? void 0 : _b.call(_a, n3)) ?? -1;
86
+ !l2 || l2 <= 0 || ((e2, t3) => {
87
+ if (h >= s) return false;
88
+ const n4 = d.get(e2);
89
+ if (void 0 !== n4 && t3 - n4 < i) return false;
90
+ if (!f.tryConsume(t3)) return false;
91
+ if (d.delete(e2), d.set(e2, t3), d.size > 500) {
92
+ const e3 = d.keys().next().value;
93
+ void 0 !== e3 && d.delete(e3);
94
+ }
95
+ return h += 1, true;
96
+ })(l2, r2) && ((_c = t2.addSailfishEvent) == null ? void 0 : _c.call(t2, e.EventType.SailfishCustom, { action: "hover", element_id: l2, started_at: Math.round(o3), ended_at: Math.round(r2), duration_ms: Math.max(0, Math.round(r2 - o3)) }));
97
+ }, endHover = (e2) => {
98
+ const t3 = m, n3 = p, o3 = v;
99
+ reset(), t3 && n3 && "click" !== e2 && emit(t3, o3, now());
100
+ }, onPointerOver = (e2) => {
101
+ const t3 = e2.target;
102
+ if (!(t3 instanceof Element)) return;
103
+ const n3 = (function resolveHoverAffordance(e3, t4, n4) {
104
+ let o3 = e3, r3 = 0;
105
+ for (; o3 && r3 <= t4; ) {
106
+ if (hasHoverAffordance(o3, n4)) return o3;
107
+ o3 = o3.parentElement, r3++;
108
+ }
109
+ return null;
110
+ })(t3, u, a);
111
+ var r2;
112
+ n3 ? n3 !== m && (m && endHover("switch"), r2 = n3, reset(), m = r2, v = now(), p = false, g = setTimeout(() => {
113
+ g = null, m && (function stillHovering(e3) {
114
+ try {
115
+ return e3.matches(":hover");
116
+ } catch {
117
+ return true;
118
+ }
119
+ })(m) ? (p = true, y = setTimeout(() => {
120
+ y = null, endHover("cap");
121
+ }, Math.max(0, c - o2))) : reset();
122
+ }, o2)) : m && !m.contains(t3) && endHover("leave");
123
+ }, onPointerOut = (e2) => {
124
+ const t3 = e2.relatedTarget;
125
+ !m || t3 instanceof Node && m.contains(t3) || endHover("leave");
126
+ }, onPointerDown = (e2) => {
127
+ const t3 = e2.target;
128
+ m && t3 instanceof Node && (m === t3 || m.contains(t3)) && endHover("click");
129
+ }, onVisibility = () => {
130
+ "hidden" === document.visibilityState && endHover("flush");
131
+ }, w = { capture: true, passive: true };
132
+ document.addEventListener("pointerover", onPointerOver, w), document.addEventListener("pointerout", onPointerOut, w), document.addEventListener("pointerdown", onPointerDown, w), document.addEventListener("visibilitychange", onVisibility, true), window.addEventListener("pagehide", onVisibility, true);
133
+ const teardown = () => {
134
+ endHover("flush"), reset(), document.removeEventListener("pointerover", onPointerOver, { capture: true }), document.removeEventListener("pointerout", onPointerOut, { capture: true }), document.removeEventListener("pointerdown", onPointerDown, { capture: true }), document.removeEventListener("visibilitychange", onVisibility, true), window.removeEventListener("pagehide", onVisibility, true), r === teardown && (r = null);
135
+ };
136
+ return r = teardown, teardown;
137
+ };
@@ -0,0 +1,139 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
3
+ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "symbol" ? key + "" : key, value);
4
+ import { EventType as e } from "@sailfish-rrweb/types";
5
+ const t = /* @__PURE__ */ new Set(["menu", "menuitem", "menubar", "tooltip"]);
6
+ let n = -1, o = [];
7
+ function collectHoverBases(e2, t2) {
8
+ for (const n2 of e2.split(",")) if (-1 !== n2.indexOf(":hover")) for (const e3 of n2.split(/\s*[>+~]\s*|\s+/)) {
9
+ if (-1 === e3.indexOf(":hover")) continue;
10
+ const n3 = e3.replace(/:hover\b/g, "").replace(/::[a-zA-Z-]+/g, "").trim();
11
+ n3 && t2.push(n3);
12
+ }
13
+ }
14
+ function collectFromRules(e2, t2, n2) {
15
+ if (!(n2 > 10)) for (let o2 = 0; o2 < e2.length; o2++) {
16
+ const r2 = e2[o2], i = r2.selectorText;
17
+ i && -1 !== i.indexOf(":hover") && collectHoverBases(i, t2);
18
+ let s = null;
19
+ try {
20
+ s = r2.cssRules ?? null;
21
+ } catch {
22
+ s = null;
23
+ }
24
+ s && s.length && collectFromRules(s, t2, n2 + 1);
25
+ }
26
+ }
27
+ function matchesHoverRule(e2) {
28
+ const t2 = (function getHoverBaseSelectors() {
29
+ const e3 = document.styleSheets;
30
+ if (e3.length === n) return o;
31
+ n = e3.length;
32
+ const t3 = [];
33
+ for (let n2 = 0; n2 < e3.length; n2++) {
34
+ let o2 = null;
35
+ try {
36
+ o2 = e3[n2].cssRules;
37
+ } catch {
38
+ continue;
39
+ }
40
+ o2 && collectFromRules(o2, t3, 0);
41
+ }
42
+ return o = t3, t3;
43
+ })();
44
+ for (let n2 = 0; n2 < t2.length; n2++) try {
45
+ if (e2.matches(t2[n2])) return true;
46
+ } catch {
47
+ }
48
+ return false;
49
+ }
50
+ function hasHoverAffordance(e2, n2) {
51
+ if (e2.hasAttribute("title") || e2.hasAttribute("data-tooltip") || e2.hasAttribute("aria-describedby")) return true;
52
+ const o2 = e2.getAttribute("role");
53
+ if (e2.hasAttribute("aria-haspopup") || o2 && t.has(o2)) return true;
54
+ if (matchesHoverRule(e2)) return true;
55
+ if (!n2) try {
56
+ if ("function" == typeof window.getComputedStyle && "pointer" === window.getComputedStyle(e2).cursor) return true;
57
+ } catch {
58
+ }
59
+ return false;
60
+ }
61
+ class TokenBucket {
62
+ constructor(e2, t2, n2) {
63
+ __publicField(this, "tokens");
64
+ __publicField(this, "last");
65
+ this.capacity = e2, this.refillPerSec = t2, this.tokens = e2, this.last = n2;
66
+ }
67
+ tryConsume(e2) {
68
+ return this.tokens = Math.min(this.capacity, this.tokens + (e2 - this.last) / 1e3 * this.refillPerSec), this.last = e2, !(this.tokens < 1) && (this.tokens -= 1, true);
69
+ }
70
+ }
71
+ let r = null;
72
+ function installHoverDetection(t2, n2) {
73
+ if ("undefined" == typeof window || "undefined" == typeof document) return () => {
74
+ };
75
+ r && (r(), r = null);
76
+ const o2 = Math.max(0, n2.dwellMs), i = Math.max(0, n2.cooldownMs ?? 3e3), s = n2.maxPerSession ?? 300, l = n2.maxPerSecond ?? 1, c = n2.maxDwellMs ?? 6e4, a = n2.maxAncestorDepth ?? 5, u = n2.strictAffordance ?? true, now = () => Date.now(), d = /* @__PURE__ */ new Map();
77
+ let h = 0;
78
+ const f = new TokenBucket(Math.max(10, l), l, now());
79
+ let m = null, v = 0, p = false, g = null, w = null;
80
+ const reset = () => {
81
+ null !== g && (clearTimeout(g), g = null), null !== w && (clearTimeout(w), w = null), m = null, p = false;
82
+ }, emit = (n3, o3, r2) => {
83
+ var _a, _b, _c;
84
+ const l2 = ((_b = (_a = t2.mirror) == null ? void 0 : _a.getId) == null ? void 0 : _b.call(_a, n3)) ?? -1;
85
+ !l2 || l2 <= 0 || ((e2, t3) => {
86
+ if (h >= s) return false;
87
+ const n4 = d.get(e2);
88
+ if (void 0 !== n4 && t3 - n4 < i) return false;
89
+ if (!f.tryConsume(t3)) return false;
90
+ if (d.delete(e2), d.set(e2, t3), d.size > 500) {
91
+ const e3 = d.keys().next().value;
92
+ void 0 !== e3 && d.delete(e3);
93
+ }
94
+ return h += 1, true;
95
+ })(l2, r2) && ((_c = t2.addSailfishEvent) == null ? void 0 : _c.call(t2, e.SailfishCustom, { action: "hover", element_id: l2, started_at: Math.round(o3), ended_at: Math.round(r2), duration_ms: Math.max(0, Math.round(r2 - o3)) }));
96
+ }, endHover = (e2) => {
97
+ const t3 = m, n3 = p, o3 = v;
98
+ reset(), t3 && n3 && "click" !== e2 && emit(t3, o3, now());
99
+ }, onPointerOver = (e2) => {
100
+ const t3 = e2.target;
101
+ if (!(t3 instanceof Element)) return;
102
+ const n3 = (function resolveHoverAffordance(e3, t4, n4) {
103
+ let o3 = e3, r3 = 0;
104
+ for (; o3 && r3 <= t4; ) {
105
+ if (hasHoverAffordance(o3, n4)) return o3;
106
+ o3 = o3.parentElement, r3++;
107
+ }
108
+ return null;
109
+ })(t3, a, u);
110
+ var r2;
111
+ n3 ? n3 !== m && (m && endHover("switch"), r2 = n3, reset(), m = r2, v = now(), p = false, g = setTimeout(() => {
112
+ g = null, m && (function stillHovering(e3) {
113
+ try {
114
+ return e3.matches(":hover");
115
+ } catch {
116
+ return true;
117
+ }
118
+ })(m) ? (p = true, w = setTimeout(() => {
119
+ w = null, endHover("cap");
120
+ }, Math.max(0, c - o2))) : reset();
121
+ }, o2)) : m && !m.contains(t3) && endHover("leave");
122
+ }, onPointerOut = (e2) => {
123
+ const t3 = e2.relatedTarget;
124
+ !m || t3 instanceof Node && m.contains(t3) || endHover("leave");
125
+ }, onPointerDown = (e2) => {
126
+ const t3 = e2.target;
127
+ m && t3 instanceof Node && (m === t3 || m.contains(t3)) && endHover("click");
128
+ }, onVisibility = () => {
129
+ "hidden" === document.visibilityState && endHover("flush");
130
+ }, y = { capture: true, passive: true };
131
+ document.addEventListener("pointerover", onPointerOver, y), document.addEventListener("pointerout", onPointerOut, y), document.addEventListener("pointerdown", onPointerDown, y), document.addEventListener("visibilitychange", onVisibility, true), window.addEventListener("pagehide", onVisibility, true);
132
+ const teardown = () => {
133
+ endHover("flush"), reset(), document.removeEventListener("pointerover", onPointerOver, { capture: true }), document.removeEventListener("pointerout", onPointerOut, { capture: true }), document.removeEventListener("pointerdown", onPointerDown, { capture: true }), document.removeEventListener("visibilitychange", onVisibility, true), window.removeEventListener("pagehide", onVisibility, true), r === teardown && (r = null);
134
+ };
135
+ return r = teardown, teardown;
136
+ }
137
+ export {
138
+ installHoverDetection
139
+ };