clarity-js 0.8.42 → 0.8.43

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 (114) hide show
  1. package/README.md +26 -26
  2. package/build/clarity.extended.js +1 -1
  3. package/build/clarity.insight.js +1 -1
  4. package/build/clarity.js +6043 -6030
  5. package/build/clarity.min.js +1 -1
  6. package/build/clarity.module.js +6043 -6030
  7. package/build/clarity.performance.js +1 -1
  8. package/package.json +70 -70
  9. package/rollup.config.ts +161 -161
  10. package/src/clarity.ts +65 -65
  11. package/src/core/api.ts +8 -8
  12. package/src/core/config.ts +29 -29
  13. package/src/core/copy.ts +3 -3
  14. package/src/core/dynamic.ts +13 -7
  15. package/src/core/event.ts +53 -53
  16. package/src/core/hash.ts +19 -19
  17. package/src/core/history.ts +71 -71
  18. package/src/core/index.ts +81 -81
  19. package/src/core/measure.ts +19 -19
  20. package/src/core/report.ts +28 -28
  21. package/src/core/scrub.ts +204 -202
  22. package/src/core/task.ts +181 -181
  23. package/src/core/throttle.ts +46 -46
  24. package/src/core/time.ts +26 -26
  25. package/src/core/timeout.ts +10 -10
  26. package/src/core/version.ts +2 -2
  27. package/src/data/baseline.ts +162 -162
  28. package/src/data/compress.ts +31 -31
  29. package/src/data/consent.ts +77 -77
  30. package/src/data/custom.ts +23 -23
  31. package/src/data/dimension.ts +53 -53
  32. package/src/data/encode.ts +155 -155
  33. package/src/data/envelope.ts +53 -53
  34. package/src/data/extract.ts +211 -211
  35. package/src/data/index.ts +50 -50
  36. package/src/data/limit.ts +44 -44
  37. package/src/data/metadata.ts +411 -408
  38. package/src/data/metric.ts +51 -51
  39. package/src/data/ping.ts +36 -36
  40. package/src/data/signal.ts +30 -30
  41. package/src/data/summary.ts +34 -34
  42. package/src/data/token.ts +39 -39
  43. package/src/data/upgrade.ts +44 -44
  44. package/src/data/upload.ts +333 -333
  45. package/src/data/variable.ts +83 -83
  46. package/src/diagnostic/encode.ts +40 -40
  47. package/src/diagnostic/fraud.ts +36 -36
  48. package/src/diagnostic/index.ts +13 -13
  49. package/src/diagnostic/internal.ts +28 -28
  50. package/src/diagnostic/script.ts +35 -35
  51. package/src/dynamic/agent/blank.ts +2 -2
  52. package/src/dynamic/agent/crisp.ts +40 -40
  53. package/src/dynamic/agent/encode.ts +25 -25
  54. package/src/dynamic/agent/index.ts +8 -8
  55. package/src/dynamic/agent/livechat.ts +58 -58
  56. package/src/dynamic/agent/tidio.ts +44 -44
  57. package/src/global.ts +6 -6
  58. package/src/index.ts +9 -9
  59. package/src/insight/blank.ts +14 -14
  60. package/src/insight/encode.ts +60 -60
  61. package/src/insight/snapshot.ts +114 -114
  62. package/src/interaction/change.ts +38 -38
  63. package/src/interaction/click.ts +173 -173
  64. package/src/interaction/clipboard.ts +32 -32
  65. package/src/interaction/encode.ts +210 -210
  66. package/src/interaction/index.ts +60 -60
  67. package/src/interaction/input.ts +57 -57
  68. package/src/interaction/pointer.ts +137 -137
  69. package/src/interaction/resize.ts +50 -50
  70. package/src/interaction/scroll.ts +129 -129
  71. package/src/interaction/selection.ts +66 -66
  72. package/src/interaction/submit.ts +30 -30
  73. package/src/interaction/timeline.ts +69 -69
  74. package/src/interaction/unload.ts +26 -26
  75. package/src/interaction/visibility.ts +27 -27
  76. package/src/layout/animation.ts +133 -133
  77. package/src/layout/custom.ts +42 -42
  78. package/src/layout/discover.ts +31 -31
  79. package/src/layout/document.ts +46 -46
  80. package/src/layout/dom.ts +439 -439
  81. package/src/layout/encode.ts +154 -154
  82. package/src/layout/index.ts +42 -42
  83. package/src/layout/mutation.ts +411 -411
  84. package/src/layout/node.ts +294 -294
  85. package/src/layout/offset.ts +19 -19
  86. package/src/layout/region.ts +151 -151
  87. package/src/layout/schema.ts +63 -63
  88. package/src/layout/selector.ts +82 -82
  89. package/src/layout/style.ts +159 -159
  90. package/src/layout/target.ts +32 -32
  91. package/src/layout/traverse.ts +27 -27
  92. package/src/performance/blank.ts +9 -9
  93. package/src/performance/encode.ts +31 -31
  94. package/src/performance/index.ts +12 -12
  95. package/src/performance/interaction.ts +125 -125
  96. package/src/performance/navigation.ts +31 -31
  97. package/src/performance/observer.ts +112 -112
  98. package/src/queue.ts +33 -33
  99. package/test/core.test.ts +139 -139
  100. package/test/helper.ts +162 -162
  101. package/test/html/core.html +27 -27
  102. package/test/stub.test.ts +7 -7
  103. package/test/tsconfig.test.json +5 -5
  104. package/tsconfig.json +21 -21
  105. package/tslint.json +32 -32
  106. package/types/agent.d.ts +39 -39
  107. package/types/core.d.ts +150 -150
  108. package/types/data.d.ts +572 -571
  109. package/types/diagnostic.d.ts +24 -24
  110. package/types/global.d.ts +30 -30
  111. package/types/index.d.ts +40 -40
  112. package/types/interaction.d.ts +177 -177
  113. package/types/layout.d.ts +276 -276
  114. package/types/performance.d.ts +31 -31
package/src/clarity.ts CHANGED
@@ -1,66 +1,66 @@
1
- import { Config, Module } from "@clarity-types/core";
2
- import { Constant } from "@clarity-types/data";
3
- import * as queue from "@src/queue";
4
- import * as core from "@src/core";
5
- import measure from "@src/core/measure";
6
- import * as task from "@src/core/task";
7
- import version from "@src/core/version";
8
- import * as data from "@src/data";
9
- import * as diagnostic from "@src/diagnostic";
10
- import * as interaction from "@src/interaction";
11
- import * as layout from "@src/layout";
12
- import * as performance from "@src/performance";
13
- import * as dynamic from "@src/core/dynamic";
14
- export { version };
15
- export { consent, consentv2, event, identify, set, upgrade, metadata, signal, maxMetric, dlog } from "@src/data";
16
- export { queue } from "@src/data/upload";
17
- export { register } from "@src/core/dynamic";
18
- export { schedule } from "@src/core/task";
19
- export { time } from "@src/core/time";
20
- export { hashText } from "@src/layout";
21
- export { measure };
22
- const modules: Module[] = [diagnostic, layout, interaction, performance, dynamic];
23
-
24
- export function start(config: Config = null): void {
25
- // Check that browser supports required APIs and we do not attempt to start Clarity multiple times
26
- if (core.check()) {
27
- core.config(config);
28
- core.start();
29
- data.start();
30
- modules.forEach(x => measure(x.start)());
31
-
32
- // If it's an internal call to start, without explicit configuration,
33
- // re-process any newly accumulated items in the queue
34
- if (config === null) { queue.process(); }
35
- }
36
- }
37
-
38
- // By default Clarity is asynchronous and will yield by looking for requestIdleCallback.
39
- // However, there can still be situations with single page apps where a user action can result
40
- // in the whole DOM being destroyed and reconstructed. While Clarity will perform favorably out of the box,
41
- // we do allow external clients to manually pause Clarity for that short burst of time and minimize
42
- // performance impact even further. For reference, we are talking single digit milliseconds optimization here, not seconds.
43
- export function pause(): void {
44
- if (core.active()) {
45
- data.event(Constant.Clarity, Constant.Pause);
46
- task.pause();
47
- }
48
- }
49
-
50
- // This is how external clients can get out of pause state, and resume Clarity to continue monitoring the page
51
- export function resume(): void {
52
- if (core.active()) {
53
- task.resume();
54
- data.event(Constant.Clarity, Constant.Resume);
55
- }
56
- }
57
-
58
- export function stop(): void {
59
- if (core.active()) {
60
- // Stop modules in the reverse order of their initialization and start queuing up items again
61
- modules.slice().reverse().forEach(x => measure(x.stop)());
62
- data.stop();
63
- core.stop();
64
- queue.setup();
65
- }
1
+ import { Config, Module } from "@clarity-types/core";
2
+ import { Constant } from "@clarity-types/data";
3
+ import * as queue from "@src/queue";
4
+ import * as core from "@src/core";
5
+ import measure from "@src/core/measure";
6
+ import * as task from "@src/core/task";
7
+ import version from "@src/core/version";
8
+ import * as data from "@src/data";
9
+ import * as diagnostic from "@src/diagnostic";
10
+ import * as interaction from "@src/interaction";
11
+ import * as layout from "@src/layout";
12
+ import * as performance from "@src/performance";
13
+ import * as dynamic from "@src/core/dynamic";
14
+ export { version };
15
+ export { consent, consentv2, event, identify, set, upgrade, metadata, signal, maxMetric, dlog } from "@src/data";
16
+ export { queue } from "@src/data/upload";
17
+ export { register } from "@src/core/dynamic";
18
+ export { schedule } from "@src/core/task";
19
+ export { time } from "@src/core/time";
20
+ export { hashText } from "@src/layout";
21
+ export { measure };
22
+ const modules: Module[] = [diagnostic, layout, interaction, performance, dynamic];
23
+
24
+ export function start(config: Config = null): void {
25
+ // Check that browser supports required APIs and we do not attempt to start Clarity multiple times
26
+ if (core.check()) {
27
+ core.config(config);
28
+ core.start();
29
+ data.start();
30
+ modules.forEach(x => measure(x.start)());
31
+
32
+ // If it's an internal call to start, without explicit configuration,
33
+ // re-process any newly accumulated items in the queue
34
+ if (config === null) { queue.process(); }
35
+ }
36
+ }
37
+
38
+ // By default Clarity is asynchronous and will yield by looking for requestIdleCallback.
39
+ // However, there can still be situations with single page apps where a user action can result
40
+ // in the whole DOM being destroyed and reconstructed. While Clarity will perform favorably out of the box,
41
+ // we do allow external clients to manually pause Clarity for that short burst of time and minimize
42
+ // performance impact even further. For reference, we are talking single digit milliseconds optimization here, not seconds.
43
+ export function pause(): void {
44
+ if (core.active()) {
45
+ data.event(Constant.Clarity, Constant.Pause);
46
+ task.pause();
47
+ }
48
+ }
49
+
50
+ // This is how external clients can get out of pause state, and resume Clarity to continue monitoring the page
51
+ export function resume(): void {
52
+ if (core.active()) {
53
+ task.resume();
54
+ data.event(Constant.Clarity, Constant.Resume);
55
+ }
56
+ }
57
+
58
+ export function stop(): void {
59
+ if (core.active()) {
60
+ // Stop modules in the reverse order of their initialization and start queuing up items again
61
+ modules.slice().reverse().forEach(x => measure(x.stop)());
62
+ data.stop();
63
+ core.stop();
64
+ queue.setup();
65
+ }
66
66
  }
package/src/core/api.ts CHANGED
@@ -1,8 +1,8 @@
1
- import { Constant } from "@clarity-types/core";
2
-
3
- export default function api(method: string): string {
4
- // Zone.js, a popular package for Angular, overrides native browser APIs which can lead to inconsistent state for single page applications.
5
- // Example issue: https://github.com/angular/angular/issues/31712
6
- // As a work around, we ensuring Clarity access APIs outside of Zone (and use native implementation instead)
7
- return window[Constant.Zone] && Constant.Symbol in window[Constant.Zone] ? window[Constant.Zone][Constant.Symbol](method) : method;
8
- }
1
+ import { Constant } from "@clarity-types/core";
2
+
3
+ export default function api(method: string): string {
4
+ // Zone.js, a popular package for Angular, overrides native browser APIs which can lead to inconsistent state for single page applications.
5
+ // Example issue: https://github.com/angular/angular/issues/31712
6
+ // As a work around, we ensuring Clarity access APIs outside of Zone (and use native implementation instead)
7
+ return window[Constant.Zone] && Constant.Symbol in window[Constant.Zone] ? window[Constant.Zone][Constant.Symbol](method) : method;
8
+ }
@@ -1,29 +1,29 @@
1
- import { Config, Time } from "@clarity-types/core";
2
-
3
- let config: Config = {
4
- projectId: null,
5
- delay: 1 * Time.Second,
6
- lean: false,
7
- lite: false,
8
- track: true,
9
- content: true,
10
- drop: [],
11
- mask: [],
12
- unmask: [],
13
- regions: [],
14
- cookies: [],
15
- fraud: true,
16
- checksum: [],
17
- report: null,
18
- upload: null,
19
- fallback: null,
20
- upgrade: null,
21
- action: null,
22
- dob: null,
23
- delayDom: false,
24
- throttleDom: true,
25
- conversions: false,
26
- includeSubdomains: true,
27
- };
28
-
29
- export default config;
1
+ import { Config, Time } from "@clarity-types/core";
2
+
3
+ let config: Config = {
4
+ projectId: null,
5
+ delay: 1 * Time.Second,
6
+ lean: false,
7
+ lite: false,
8
+ track: true,
9
+ content: true,
10
+ drop: [],
11
+ mask: [],
12
+ unmask: [],
13
+ regions: [],
14
+ cookies: [],
15
+ fraud: true,
16
+ checksum: [],
17
+ report: null,
18
+ upload: null,
19
+ fallback: null,
20
+ upgrade: null,
21
+ action: null,
22
+ dob: null,
23
+ delayDom: false,
24
+ throttleDom: true,
25
+ conversions: false,
26
+ includeSubdomains: true,
27
+ };
28
+
29
+ export default config;
package/src/core/copy.ts CHANGED
@@ -1,3 +1,3 @@
1
- export default function<T>(input: T): T {
2
- return JSON.parse(JSON.stringify(input));
3
- }
1
+ export default function<T>(input: T): T {
2
+ return JSON.parse(JSON.stringify(input));
3
+ }
@@ -1,5 +1,7 @@
1
1
  import { Constant } from "@clarity-types/layout";
2
+ import { Constant as DataConstant } from "@clarity-types/data";
2
3
  import * as baseline from "@src/data/baseline";
4
+ import { report } from "@src/core/report";
3
5
 
4
6
  let stopCallbacks: (() => void)[] = [];
5
7
  let active = false;
@@ -37,19 +39,23 @@ export function event(signal: string): void {
37
39
  return;
38
40
  }
39
41
 
40
- load(parts[0]);
41
-
42
- if (m) {
43
- modules.add(m);
44
- baseline.dynamic(modules);
45
- }
42
+ load(parts[0], m);
46
43
  }
47
44
 
48
- function load(url: string): void {
45
+ function load(url: string, mid: number | null): void {
49
46
  try {
50
47
  const script = document.createElement("script");
51
48
  script.src = url;
52
49
  script.async = true;
50
+ script.onload = () => {
51
+ if (mid) {
52
+ modules.add(mid);
53
+ baseline.dynamic(modules);
54
+ }
55
+ };
56
+ script.onerror = () => {
57
+ report(new Error(`${DataConstant.Module}: ${url}`));
58
+ };
53
59
  document.head.appendChild(script);
54
60
  } catch (error) {
55
61
  // Do nothing
package/src/core/event.ts CHANGED
@@ -1,53 +1,53 @@
1
- import { BrowserEvent, Constant } from "@clarity-types/core";
2
- import api from "./api";
3
- import measure from "./measure";
4
-
5
- let bindings: Map<EventTarget, BrowserEvent[]> = new Map();
6
-
7
- export function bind(target: EventTarget, event: string, listener: EventListener, capture: boolean = false, passive: boolean = true): void {
8
- listener = measure(listener) as EventListener;
9
- // Wrapping following lines inside try / catch to cover edge cases where we might try to access an inaccessible element.
10
- // E.g. Iframe may start off as same-origin but later turn into cross-origin, and the following lines will throw an exception.
11
- try {
12
- target[api(Constant.AddEventListener)](event, listener, { capture, passive });
13
- if (!has(target)) {
14
- bindings.set(target, []);
15
- }
16
-
17
- bindings.get(target).push({ event, listener, options: { capture, passive } });
18
- } catch {
19
- /* do nothing */
20
- }
21
- }
22
-
23
- export function reset(): void {
24
- // Walk through existing list of bindings and remove them all
25
- bindings.forEach((bindingsPerTarget: BrowserEvent[], target: EventTarget) => {
26
- resetByTarget(bindingsPerTarget, target);
27
- });
28
-
29
- bindings = new Map();
30
- }
31
-
32
- export function unbind(target: EventTarget) {
33
- if (!has(target)) {
34
- return;
35
- }
36
- resetByTarget(bindings.get(target), target);
37
- }
38
-
39
- export function has(target: EventTarget): boolean {
40
- return bindings.has(target);
41
- }
42
-
43
- function resetByTarget(bindingsPerTarget: BrowserEvent[], target: EventTarget): void {
44
- bindingsPerTarget.forEach((binding) => {
45
- // Wrapping inside try / catch to avoid situations where the element may be destroyed before we get a chance to unbind
46
- try {
47
- target[api(Constant.RemoveEventListener)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
48
- } catch {
49
- /* do nothing */
50
- }
51
- });
52
- bindings.delete(target);
53
- }
1
+ import { BrowserEvent, Constant } from "@clarity-types/core";
2
+ import api from "./api";
3
+ import measure from "./measure";
4
+
5
+ let bindings: Map<EventTarget, BrowserEvent[]> = new Map();
6
+
7
+ export function bind(target: EventTarget, event: string, listener: EventListener, capture: boolean = false, passive: boolean = true): void {
8
+ listener = measure(listener) as EventListener;
9
+ // Wrapping following lines inside try / catch to cover edge cases where we might try to access an inaccessible element.
10
+ // E.g. Iframe may start off as same-origin but later turn into cross-origin, and the following lines will throw an exception.
11
+ try {
12
+ target[api(Constant.AddEventListener)](event, listener, { capture, passive });
13
+ if (!has(target)) {
14
+ bindings.set(target, []);
15
+ }
16
+
17
+ bindings.get(target).push({ event, listener, options: { capture, passive } });
18
+ } catch {
19
+ /* do nothing */
20
+ }
21
+ }
22
+
23
+ export function reset(): void {
24
+ // Walk through existing list of bindings and remove them all
25
+ bindings.forEach((bindingsPerTarget: BrowserEvent[], target: EventTarget) => {
26
+ resetByTarget(bindingsPerTarget, target);
27
+ });
28
+
29
+ bindings = new Map();
30
+ }
31
+
32
+ export function unbind(target: EventTarget) {
33
+ if (!has(target)) {
34
+ return;
35
+ }
36
+ resetByTarget(bindings.get(target), target);
37
+ }
38
+
39
+ export function has(target: EventTarget): boolean {
40
+ return bindings.has(target);
41
+ }
42
+
43
+ function resetByTarget(bindingsPerTarget: BrowserEvent[], target: EventTarget): void {
44
+ bindingsPerTarget.forEach((binding) => {
45
+ // Wrapping inside try / catch to avoid situations where the element may be destroyed before we get a chance to unbind
46
+ try {
47
+ target[api(Constant.RemoveEventListener)](binding.event, binding.listener, { capture: binding.options.capture, passive: binding.options.passive });
48
+ } catch {
49
+ /* do nothing */
50
+ }
51
+ });
52
+ bindings.delete(target);
53
+ }
package/src/core/hash.ts CHANGED
@@ -1,19 +1,19 @@
1
- // tslint:disable: no-bitwise
2
- export default function(input: string, precision: number = null): string {
3
- // Code inspired from C# GetHashCode: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs
4
- let hash = 0;
5
- let hashOne = 5381;
6
- let hashTwo = hashOne;
7
- for (let i = 0; i < input.length; i += 2) {
8
- let charOne = input.charCodeAt(i);
9
- hashOne = ((hashOne << 5) + hashOne) ^ charOne;
10
- if (i + 1 < input.length) {
11
- let charTwo = input.charCodeAt(i + 1);
12
- hashTwo = ((hashTwo << 5) + hashTwo) ^ charTwo;
13
- }
14
- }
15
- // Replace the magic number from C# implementation (1566083941) with a smaller prime number (11579)
16
- // This ensures we don't hit integer overflow and prevent collisions
17
- hash = Math.abs(hashOne + (hashTwo * 11579));
18
- return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
19
- }
1
+ // tslint:disable: no-bitwise
2
+ export default function(input: string, precision: number = null): string {
3
+ // Code inspired from C# GetHashCode: https://github.com/Microsoft/referencesource/blob/master/mscorlib/system/string.cs
4
+ let hash = 0;
5
+ let hashOne = 5381;
6
+ let hashTwo = hashOne;
7
+ for (let i = 0; i < input.length; i += 2) {
8
+ let charOne = input.charCodeAt(i);
9
+ hashOne = ((hashOne << 5) + hashOne) ^ charOne;
10
+ if (i + 1 < input.length) {
11
+ let charTwo = input.charCodeAt(i + 1);
12
+ hashTwo = ((hashTwo << 5) + hashTwo) ^ charTwo;
13
+ }
14
+ }
15
+ // Replace the magic number from C# implementation (1566083941) with a smaller prime number (11579)
16
+ // This ensures we don't hit integer overflow and prevent collisions
17
+ hash = Math.abs(hashOne + (hashTwo * 11579));
18
+ return (precision ? hash % Math.pow(2, precision) : hash).toString(36);
19
+ }
@@ -1,71 +1,71 @@
1
- import { BooleanFlag, Code, Constant, Metric, Setting, Severity } from "@clarity-types/data";
2
- import * as clarity from "@src/clarity";
3
- import * as core from "@src/core"
4
- import { bind } from "@src/core/event";
5
- import * as internal from "@src/diagnostic/internal";
6
- import * as metric from "@src/data/metric";
7
-
8
- let pushState = null;
9
- let replaceState = null;
10
- let url = null;
11
- let count = 0;
12
-
13
- export function start(): void {
14
- url = getCurrentUrl();
15
- count = 0;
16
- bind(window, "popstate", compute);
17
-
18
- // Add a proxy to history.pushState function
19
- if (pushState === null) {
20
- pushState = history.pushState;
21
- history.pushState = function(): void {
22
- pushState.apply(this, arguments);
23
- if (core.active() && check()) {
24
- compute();
25
- }
26
- };
27
- }
28
-
29
- // Add a proxy to history.replaceState function
30
- if (replaceState === null)
31
- {
32
- replaceState = history.replaceState;
33
- history.replaceState = function(): void {
34
- replaceState.apply(this, arguments);
35
- if (core.active() && check()) {
36
- compute();
37
- }
38
- };
39
- }
40
- }
41
-
42
- function check(): boolean {
43
- if (count++ > Setting.CallStackDepth) {
44
- internal.log(Code.CallStackDepth, Severity.Info);
45
- return false;
46
- }
47
- return true;
48
- }
49
-
50
- function compute(): void {
51
- count = 0; // Reset the counter
52
- if (url !== getCurrentUrl()) {
53
- // If the url changed, start tracking it as a new page
54
- clarity.stop();
55
- window.setTimeout(restart, Setting.RestartDelay);
56
- }
57
- }
58
-
59
- function restart(): void {
60
- clarity.start();
61
- metric.max(Metric.SinglePage, BooleanFlag.True);
62
- }
63
-
64
- function getCurrentUrl(): string {
65
- return location.href ? location.href.replace(location.hash, Constant.Empty) : location.href;
66
- }
67
-
68
- export function stop(): void {
69
- url = null;
70
- count = 0;
71
- }
1
+ import { BooleanFlag, Code, Constant, Metric, Setting, Severity } from "@clarity-types/data";
2
+ import * as clarity from "@src/clarity";
3
+ import * as core from "@src/core"
4
+ import { bind } from "@src/core/event";
5
+ import * as internal from "@src/diagnostic/internal";
6
+ import * as metric from "@src/data/metric";
7
+
8
+ let pushState = null;
9
+ let replaceState = null;
10
+ let url = null;
11
+ let count = 0;
12
+
13
+ export function start(): void {
14
+ url = getCurrentUrl();
15
+ count = 0;
16
+ bind(window, "popstate", compute);
17
+
18
+ // Add a proxy to history.pushState function
19
+ if (pushState === null) {
20
+ pushState = history.pushState;
21
+ history.pushState = function(): void {
22
+ pushState.apply(this, arguments);
23
+ if (core.active() && check()) {
24
+ compute();
25
+ }
26
+ };
27
+ }
28
+
29
+ // Add a proxy to history.replaceState function
30
+ if (replaceState === null)
31
+ {
32
+ replaceState = history.replaceState;
33
+ history.replaceState = function(): void {
34
+ replaceState.apply(this, arguments);
35
+ if (core.active() && check()) {
36
+ compute();
37
+ }
38
+ };
39
+ }
40
+ }
41
+
42
+ function check(): boolean {
43
+ if (count++ > Setting.CallStackDepth) {
44
+ internal.log(Code.CallStackDepth, Severity.Info);
45
+ return false;
46
+ }
47
+ return true;
48
+ }
49
+
50
+ function compute(): void {
51
+ count = 0; // Reset the counter
52
+ if (url !== getCurrentUrl()) {
53
+ // If the url changed, start tracking it as a new page
54
+ clarity.stop();
55
+ window.setTimeout(restart, Setting.RestartDelay);
56
+ }
57
+ }
58
+
59
+ function restart(): void {
60
+ clarity.start();
61
+ metric.max(Metric.SinglePage, BooleanFlag.True);
62
+ }
63
+
64
+ function getCurrentUrl(): string {
65
+ return location.href ? location.href.replace(location.hash, Constant.Empty) : location.href;
66
+ }
67
+
68
+ export function stop(): void {
69
+ url = null;
70
+ count = 0;
71
+ }