@openreplay/tracker 3.5.12 → 3.5.14

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 (70) hide show
  1. package/cjs/app/guards.d.ts +18 -0
  2. package/cjs/app/guards.js +24 -0
  3. package/cjs/app/index.d.ts +5 -1
  4. package/cjs/app/index.js +55 -32
  5. package/cjs/app/nodes.d.ts +3 -3
  6. package/cjs/app/nodes.js +2 -2
  7. package/cjs/app/observer/observer.d.ts +1 -2
  8. package/cjs/app/observer/observer.js +73 -60
  9. package/cjs/app/observer/top_observer.js +3 -3
  10. package/cjs/app/sanitizer.d.ts +3 -1
  11. package/cjs/app/sanitizer.js +13 -2
  12. package/cjs/app/session.d.ts +2 -7
  13. package/cjs/app/session.js +24 -37
  14. package/cjs/index.js +2 -2
  15. package/cjs/modules/console.d.ts +1 -1
  16. package/cjs/modules/console.js +2 -1
  17. package/cjs/modules/cssrules.d.ts +1 -1
  18. package/cjs/modules/cssrules.js +2 -4
  19. package/cjs/modules/exception.d.ts +1 -1
  20. package/cjs/modules/img.d.ts +1 -1
  21. package/cjs/modules/img.js +14 -6
  22. package/cjs/modules/input.d.ts +2 -1
  23. package/cjs/modules/input.js +18 -20
  24. package/cjs/modules/longtasks.d.ts +1 -1
  25. package/cjs/modules/mouse.d.ts +1 -1
  26. package/cjs/modules/mouse.js +3 -2
  27. package/cjs/modules/performance.d.ts +1 -1
  28. package/cjs/modules/scroll.d.ts +1 -1
  29. package/cjs/modules/scroll.js +3 -2
  30. package/cjs/modules/timing.d.ts +1 -1
  31. package/cjs/modules/timing.js +2 -1
  32. package/cjs/modules/viewport.d.ts +1 -1
  33. package/lib/app/guards.d.ts +18 -0
  34. package/lib/app/guards.js +16 -0
  35. package/lib/app/index.d.ts +5 -1
  36. package/lib/app/index.js +56 -33
  37. package/lib/app/nodes.d.ts +3 -3
  38. package/lib/app/nodes.js +2 -2
  39. package/lib/app/observer/observer.d.ts +1 -2
  40. package/lib/app/observer/observer.js +70 -57
  41. package/lib/app/observer/top_observer.js +3 -3
  42. package/lib/app/sanitizer.d.ts +3 -1
  43. package/lib/app/sanitizer.js +13 -2
  44. package/lib/app/session.d.ts +2 -7
  45. package/lib/app/session.js +24 -37
  46. package/lib/common/tsconfig.tsbuildinfo +1 -1
  47. package/lib/index.js +2 -2
  48. package/lib/modules/console.d.ts +1 -1
  49. package/lib/modules/console.js +2 -1
  50. package/lib/modules/cssrules.d.ts +1 -1
  51. package/lib/modules/cssrules.js +2 -4
  52. package/lib/modules/exception.d.ts +1 -1
  53. package/lib/modules/img.d.ts +1 -1
  54. package/lib/modules/img.js +14 -6
  55. package/lib/modules/input.d.ts +2 -1
  56. package/lib/modules/input.js +18 -20
  57. package/lib/modules/longtasks.d.ts +1 -1
  58. package/lib/modules/mouse.d.ts +1 -1
  59. package/lib/modules/mouse.js +3 -2
  60. package/lib/modules/performance.d.ts +1 -1
  61. package/lib/modules/scroll.d.ts +1 -1
  62. package/lib/modules/scroll.js +3 -2
  63. package/lib/modules/timing.d.ts +1 -1
  64. package/lib/modules/timing.js +2 -1
  65. package/lib/modules/viewport.d.ts +1 -1
  66. package/package.json +7 -7
  67. package/cjs/app/context.d.ts +0 -18
  68. package/cjs/app/context.js +0 -73
  69. package/lib/app/context.d.ts +0 -18
  70. package/lib/app/context.js +0 -68
@@ -1,4 +1,5 @@
1
1
  import { CSSInsertRuleURLBased, CSSDeleteRule, TechnicalInfo } from "../common/messages.js";
2
+ import { hasTag } from "../app/guards.js";
2
3
  export default function (app) {
3
4
  if (app === null) {
4
5
  return;
@@ -30,10 +31,7 @@ export default function (app) {
30
31
  return deleteRule.call(this, index);
31
32
  };
32
33
  app.nodes.attachNodeCallback((node) => {
33
- if (!(node instanceof HTMLStyleElement)) {
34
- return;
35
- }
36
- if (!(node.sheet instanceof CSSStyleSheet)) {
34
+ if (!hasTag(node, "STYLE") || !node.sheet) {
37
35
  return;
38
36
  }
39
37
  if (node.textContent !== null && node.textContent.trim().length > 0) {
@@ -1,5 +1,5 @@
1
+ import type App from "../app/index.js";
1
2
  import type Message from "../common/messages.js";
2
- import App from "../app/index.js";
3
3
  export interface Options {
4
4
  captureExceptions: boolean;
5
5
  }
@@ -1,2 +1,2 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export default function (app: App): void;
@@ -1,5 +1,6 @@
1
1
  import { timestamp, isURL } from "../utils.js";
2
2
  import { ResourceTiming, SetNodeAttributeURLBased, SetNodeAttribute } from "../common/messages.js";
3
+ import { hasTag } from "../app/guards.js";
3
4
  const PLACEHOLDER_SRC = "https://static.openreplay.com/tracker/placeholder.jpeg";
4
5
  export default function (app) {
5
6
  function sendPlaceholder(id, node) {
@@ -17,7 +18,7 @@ export default function (app) {
17
18
  if (id === undefined) {
18
19
  return;
19
20
  }
20
- const { src, complete, naturalWidth, naturalHeight } = this;
21
+ const { src, complete, naturalWidth, naturalHeight, srcset } = this;
21
22
  if (!complete) {
22
23
  return;
23
24
  }
@@ -31,28 +32,35 @@ export default function (app) {
31
32
  }
32
33
  else {
33
34
  app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
35
+ srcset && app.send(new SetNodeAttribute(id, 'srcset', srcset));
34
36
  }
35
37
  });
36
38
  const observer = new MutationObserver((mutations) => {
37
39
  for (const mutation of mutations) {
38
- if (mutation.type === "attributes" && mutation.attributeName === "src") {
40
+ if (mutation.type === "attributes") {
39
41
  const target = mutation.target;
40
42
  const id = app.nodes.getID(target);
41
43
  if (id === undefined) {
42
44
  return;
43
45
  }
44
- const src = target.src;
45
- app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
46
+ if (mutation.attributeName === "src") {
47
+ const src = target.src;
48
+ app.send(new SetNodeAttributeURLBased(id, 'src', src, app.getBaseHref()));
49
+ }
50
+ if (mutation.attributeName === "srcset") {
51
+ const srcset = target.srcset;
52
+ app.send(new SetNodeAttribute(id, 'srcset', srcset));
53
+ }
46
54
  }
47
55
  }
48
56
  });
49
57
  app.nodes.attachNodeCallback((node) => {
50
- if (!(node instanceof HTMLImageElement)) {
58
+ if (!hasTag(node, "IMG")) {
51
59
  return;
52
60
  }
53
61
  app.nodes.attachElementListener('error', node, sendImgSrc);
54
62
  app.nodes.attachElementListener('load', node, sendImgSrc);
55
63
  sendImgSrc.call(node);
56
- observer.observe(node, { attributes: true });
64
+ observer.observe(node, { attributes: true, attributeFilter: ["src", "srcset"] });
57
65
  });
58
66
  }
@@ -1,4 +1,4 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  declare type TextEditableElement = HTMLInputElement | HTMLTextAreaElement;
3
3
  export declare function getInputLabel(node: TextEditableElement): string;
4
4
  export declare const enum InputMode {
@@ -10,6 +10,7 @@ export interface Options {
10
10
  obscureInputNumbers: boolean;
11
11
  obscureInputEmails: boolean;
12
12
  defaultInputMode: InputMode;
13
+ obscureInputDates: boolean;
13
14
  }
14
15
  export default function (app: App, opts: Partial<Options>): void;
15
16
  export {};
@@ -1,22 +1,18 @@
1
1
  import { normSpaces, IN_BROWSER, getLabelAttribute, hasOpenreplayAttribute, } from "../utils.js";
2
+ import { hasTag } from "../app/guards.js";
2
3
  import { SetInputTarget, SetInputValue, SetInputChecked } from "../common/messages.js";
4
+ const INPUT_TYPES = ['text', 'password', 'email', 'search', 'number', 'range', 'date'];
3
5
  function isTextEditable(node) {
4
- if (node instanceof HTMLTextAreaElement) {
6
+ if (hasTag(node, "TEXTAREA")) {
5
7
  return true;
6
8
  }
7
- if (!(node instanceof HTMLInputElement)) {
9
+ if (!hasTag(node, "INPUT")) {
8
10
  return false;
9
11
  }
10
- const type = node.type;
11
- return (type === 'text' ||
12
- type === 'password' ||
13
- type === 'email' ||
14
- type === 'search' ||
15
- type === 'number' ||
16
- type === 'range');
12
+ return INPUT_TYPES.includes(node.type);
17
13
  }
18
14
  function isCheckable(node) {
19
- if (!(node instanceof HTMLInputElement)) {
15
+ if (!hasTag(node, "INPUT")) {
20
16
  return false;
21
17
  }
22
18
  const type = node.type;
@@ -26,7 +22,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
26
22
  ? (node) => {
27
23
  let p = node;
28
24
  while ((p = p.parentNode) !== null) {
29
- if (p instanceof HTMLLabelElement) {
25
+ if (hasTag(p, "LABEL")) {
30
26
  return p;
31
27
  }
32
28
  }
@@ -38,7 +34,7 @@ const labelElementFor = IN_BROWSER && 'labels' in HTMLInputElement.prototype
38
34
  : (node) => {
39
35
  let p = node;
40
36
  while ((p = p.parentNode) !== null) {
41
- if (p instanceof HTMLLabelElement) {
37
+ if (hasTag(p, "LABEL")) {
42
38
  return p;
43
39
  }
44
40
  }
@@ -67,7 +63,8 @@ export default function (app, opts) {
67
63
  const options = Object.assign({
68
64
  obscureInputNumbers: true,
69
65
  obscureInputEmails: true,
70
- defaultInputMode: 0 /* Plain */,
66
+ defaultInputMode: 0 /* InputMode.Plain */,
67
+ obscureInputDates: false,
71
68
  }, opts);
72
69
  function sendInputTarget(id, node) {
73
70
  const label = getInputLabel(node);
@@ -79,22 +76,23 @@ export default function (app, opts) {
79
76
  let value = node.value;
80
77
  let inputMode = options.defaultInputMode;
81
78
  if (node.type === 'password' || hasOpenreplayAttribute(node, 'hidden')) {
82
- inputMode = 2 /* Hidden */;
79
+ inputMode = 2 /* InputMode.Hidden */;
83
80
  }
84
81
  else if (hasOpenreplayAttribute(node, 'obscured') ||
85
- (inputMode === 0 /* Plain */ &&
86
- ((options.obscureInputNumbers && /\d\d\d\d/.test(value)) ||
82
+ (inputMode === 0 /* InputMode.Plain */ &&
83
+ ((options.obscureInputNumbers && node.type !== 'date' && /\d\d\d\d/.test(value)) ||
84
+ (options.obscureInputDates && node.type === 'date') ||
87
85
  (options.obscureInputEmails &&
88
86
  (node.type === 'email' || !!~value.indexOf('@')))))) {
89
- inputMode = 1 /* Obscured */;
87
+ inputMode = 1 /* InputMode.Obscured */;
90
88
  }
91
89
  let mask = 0;
92
90
  switch (inputMode) {
93
- case 2 /* Hidden */:
91
+ case 2 /* InputMode.Hidden */:
94
92
  mask = -1;
95
93
  value = '';
96
94
  break;
97
- case 1 /* Obscured */:
95
+ case 1 /* InputMode.Obscured */:
98
96
  mask = value.length;
99
97
  value = '';
100
98
  break;
@@ -144,7 +142,7 @@ export default function (app, opts) {
144
142
  return;
145
143
  }
146
144
  // TODO: support multiple select (?): use selectedOptions; Need send target?
147
- if (node instanceof HTMLSelectElement) {
145
+ if (hasTag(node, "SELECT")) {
148
146
  sendInputValue(id, node);
149
147
  app.attachEventListener(node, "change", () => {
150
148
  sendInputValue(id, node);
@@ -1,2 +1,2 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export default function (app: App): void;
@@ -1,2 +1,2 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export default function (app: App): void;
@@ -1,3 +1,4 @@
1
+ import { hasTag, isSVGElement } from "../app/guards.js";
1
2
  import { normSpaces, hasOpenreplayAttribute, getLabelAttribute, } from "../utils.js";
2
3
  import { MouseMove, MouseClick } from "../common/messages.js";
3
4
  import { getInputLabel } from "./input.js";
@@ -47,7 +48,7 @@ function _getTarget(target) {
47
48
  }
48
49
  element = element.parentElement;
49
50
  }
50
- if (target instanceof SVGElement) {
51
+ if (isSVGElement(target)) {
51
52
  let owner = target.ownerSVGElement;
52
53
  while (owner !== null) {
53
54
  target = owner;
@@ -77,7 +78,7 @@ export default function (app) {
77
78
  if (dl !== null) {
78
79
  return dl;
79
80
  }
80
- if (target instanceof HTMLInputElement) {
81
+ if (hasTag(target, "INPUT")) {
81
82
  return getInputLabel(target);
82
83
  }
83
84
  if (isClickable(target)) {
@@ -1,4 +1,4 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export declare const deviceMemory: number;
3
3
  export declare const jsHeapSizeLimit: number;
4
4
  export interface Options {
@@ -1,2 +1,2 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export default function (app: App): void;
@@ -1,4 +1,5 @@
1
1
  import { SetViewportScroll, SetNodeScroll } from "../common/messages.js";
2
+ import { isElementNode } from "../app/guards.js";
2
3
  export default function (app) {
3
4
  let documentScroll = false;
4
5
  const nodeScroll = new Map();
@@ -20,8 +21,8 @@ export default function (app) {
20
21
  documentScroll = false;
21
22
  nodeScroll.clear();
22
23
  });
23
- app.nodes.attachNodeCallback(node => {
24
- if (node instanceof Element && node.scrollLeft + node.scrollTop > 0) {
24
+ app.nodes.attachNodeCallback((node, isStart) => {
25
+ if (isStart && isElementNode(node) && node.scrollLeft + node.scrollTop > 0) {
25
26
  nodeScroll.set(node, [node.scrollLeft, node.scrollTop]);
26
27
  }
27
28
  });
@@ -1,4 +1,4 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export interface Options {
3
3
  captureResourceTimings: boolean;
4
4
  capturePageLoadTimings: boolean;
@@ -1,3 +1,4 @@
1
+ import { hasTag } from "../app/guards.js";
1
2
  import { isURL } from "../utils.js";
2
3
  import { ResourceTiming, PageLoadTiming, PageRenderTiming } from "../common/messages.js";
3
4
  function getPaintBlocks(resources) {
@@ -7,7 +8,7 @@ function getPaintBlocks(resources) {
7
8
  for (let i = 0; i < elements.length; i++) {
8
9
  const element = elements[i];
9
10
  let src = '';
10
- if (element instanceof HTMLImageElement) {
11
+ if (hasTag(element, "IMG")) {
11
12
  src = element.currentSrc || element.src;
12
13
  }
13
14
  if (!src) {
@@ -1,2 +1,2 @@
1
- import App from "../app/index.js";
1
+ import type App from "../app/index.js";
2
2
  export default function (app: App): void;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@openreplay/tracker",
3
3
  "description": "The OpenReplay tracker main package",
4
- "version": "3.5.12",
4
+ "version": "3.5.14",
5
5
  "keywords": [
6
6
  "logging",
7
7
  "replay"
@@ -25,11 +25,11 @@
25
25
  "devDependencies": {
26
26
  "@babel/core": "^7.10.2",
27
27
  "@rollup/plugin-babel": "^5.0.3",
28
- "@rollup/plugin-node-resolve": "^8.0.1",
29
- "@typescript-eslint/eslint-plugin": "^2.34.0",
30
- "@typescript-eslint/parser": "^2.34.0",
31
- "eslint": "^6.8.0",
32
- "eslint-plugin-prettier": "^3.1.4",
28
+ "@rollup/plugin-node-resolve": "^10.0.0",
29
+ "@typescript-eslint/eslint-plugin": "^4.33.0",
30
+ "@typescript-eslint/parser": "^4.33.0",
31
+ "eslint": "^7.8.0",
32
+ "eslint-plugin-prettier": "^4.1.4",
33
33
  "prettier": "^2.0.0",
34
34
  "replace-in-files": "^2.0.3",
35
35
  "rollup": "^2.17.0",
@@ -41,6 +41,6 @@
41
41
  "error-stack-parser": "^2.0.6"
42
42
  },
43
43
  "engines": {
44
- "node": ">=12"
44
+ "node": ">=14.15"
45
45
  }
46
46
  }
@@ -1,18 +0,0 @@
1
- export interface Window extends globalThis.Window {
2
- HTMLInputElement: typeof HTMLInputElement;
3
- HTMLLinkElement: typeof HTMLLinkElement;
4
- HTMLStyleElement: typeof HTMLStyleElement;
5
- SVGStyleElement: typeof SVGStyleElement;
6
- HTMLIFrameElement: typeof HTMLIFrameElement;
7
- Text: typeof Text;
8
- Element: typeof Element;
9
- ShadowRoot: typeof ShadowRoot;
10
- }
11
- declare type WindowConstructor = Document | Element | Text | ShadowRoot | HTMLInputElement | HTMLLinkElement | HTMLStyleElement | HTMLIFrameElement;
12
- declare type Constructor<T> = {
13
- new (...args: any[]): T;
14
- name: string;
15
- };
16
- export declare function isInstance<T extends WindowConstructor>(node: Node, constr: Constructor<T>): node is T;
17
- export declare function inDocument(node: Node): boolean;
18
- export {};
@@ -1,73 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.inDocument = exports.isInstance = void 0;
4
- // TODO: we need a type expert here so we won't have to ignore the lines
5
- // TODO: use it everywhere (static function; export from which file? <-- global Window typing required)
6
- // TODO: most efficient and common way
7
- // Problem: on YouTube there is context[constr.name] undefined for constr=ShadowDom due to some minimisations
8
- function isInstance(node, constr) {
9
- const doc = node.ownerDocument;
10
- if (!doc) { // null if Document
11
- return constr.name === 'Document';
12
- }
13
- let context =
14
- // @ts-ignore (for EI, Safary)
15
- doc.parentWindow ||
16
- doc.defaultView; // TODO: smart global typing for Window object
17
- while (context !== window) {
18
- // @ts-ignore
19
- if (context[constr.name] && node instanceof context[constr.name]) {
20
- return true;
21
- }
22
- // @ts-ignore
23
- context = context.parent || window;
24
- }
25
- // @ts-ignore
26
- return context[constr.name] ? node instanceof context[constr.name] : node instanceof constr;
27
- }
28
- exports.isInstance = isInstance;
29
- // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
30
- function inDocument(node) {
31
- const doc = node.ownerDocument;
32
- if (!doc) {
33
- return true;
34
- } // Document
35
- let current = node;
36
- while (current) {
37
- if (current === doc) {
38
- return true;
39
- }
40
- else if (isInstance(current, ShadowRoot)) {
41
- current = current.host;
42
- }
43
- else {
44
- current = current.parentNode;
45
- }
46
- }
47
- return false;
48
- }
49
- exports.inDocument = inDocument;
50
- // export function inDocument(node: Node): boolean {
51
- // // @ts-ignore compatability
52
- // if (node.getRootNode) {
53
- // let root: Node
54
- // while ((root = node.getRootNode()) !== node) {
55
- // ////
56
- // }
57
- // }
58
- // const doc = node.ownerDocument
59
- // if (!doc) { return false }
60
- // if (doc.contains(node)) { return true }
61
- // let context: Window =
62
- // // @ts-ignore (for EI, Safary)
63
- // doc.parentWindow ||
64
- // doc.defaultView;
65
- // while(context.parent && context.parent !== context) {
66
- // if (context.document.contains(node)) {
67
- // return true
68
- // }
69
- // // @ts-ignore
70
- // context = context.parent
71
- // }
72
- // return false;
73
- // }
@@ -1,18 +0,0 @@
1
- export interface Window extends globalThis.Window {
2
- HTMLInputElement: typeof HTMLInputElement;
3
- HTMLLinkElement: typeof HTMLLinkElement;
4
- HTMLStyleElement: typeof HTMLStyleElement;
5
- SVGStyleElement: typeof SVGStyleElement;
6
- HTMLIFrameElement: typeof HTMLIFrameElement;
7
- Text: typeof Text;
8
- Element: typeof Element;
9
- ShadowRoot: typeof ShadowRoot;
10
- }
11
- declare type WindowConstructor = Document | Element | Text | ShadowRoot | HTMLInputElement | HTMLLinkElement | HTMLStyleElement | HTMLIFrameElement;
12
- declare type Constructor<T> = {
13
- new (...args: any[]): T;
14
- name: string;
15
- };
16
- export declare function isInstance<T extends WindowConstructor>(node: Node, constr: Constructor<T>): node is T;
17
- export declare function inDocument(node: Node): boolean;
18
- export {};
@@ -1,68 +0,0 @@
1
- // TODO: we need a type expert here so we won't have to ignore the lines
2
- // TODO: use it everywhere (static function; export from which file? <-- global Window typing required)
3
- // TODO: most efficient and common way
4
- // Problem: on YouTube there is context[constr.name] undefined for constr=ShadowDom due to some minimisations
5
- export function isInstance(node, constr) {
6
- const doc = node.ownerDocument;
7
- if (!doc) { // null if Document
8
- return constr.name === 'Document';
9
- }
10
- let context =
11
- // @ts-ignore (for EI, Safary)
12
- doc.parentWindow ||
13
- doc.defaultView; // TODO: smart global typing for Window object
14
- while (context !== window) {
15
- // @ts-ignore
16
- if (context[constr.name] && node instanceof context[constr.name]) {
17
- return true;
18
- }
19
- // @ts-ignore
20
- context = context.parent || window;
21
- }
22
- // @ts-ignore
23
- return context[constr.name] ? node instanceof context[constr.name] : node instanceof constr;
24
- }
25
- // TODO: ensure 1. it works in every cases (iframes/detached nodes) and 2. the most efficient
26
- export function inDocument(node) {
27
- const doc = node.ownerDocument;
28
- if (!doc) {
29
- return true;
30
- } // Document
31
- let current = node;
32
- while (current) {
33
- if (current === doc) {
34
- return true;
35
- }
36
- else if (isInstance(current, ShadowRoot)) {
37
- current = current.host;
38
- }
39
- else {
40
- current = current.parentNode;
41
- }
42
- }
43
- return false;
44
- }
45
- // export function inDocument(node: Node): boolean {
46
- // // @ts-ignore compatability
47
- // if (node.getRootNode) {
48
- // let root: Node
49
- // while ((root = node.getRootNode()) !== node) {
50
- // ////
51
- // }
52
- // }
53
- // const doc = node.ownerDocument
54
- // if (!doc) { return false }
55
- // if (doc.contains(node)) { return true }
56
- // let context: Window =
57
- // // @ts-ignore (for EI, Safary)
58
- // doc.parentWindow ||
59
- // doc.defaultView;
60
- // while(context.parent && context.parent !== context) {
61
- // if (context.document.contains(node)) {
62
- // return true
63
- // }
64
- // // @ts-ignore
65
- // context = context.parent
66
- // }
67
- // return false;
68
- // }